|
@@ -29,8 +29,11 @@ import java.util.Map;
|
|
|
import java.util.HashMap;
|
|
|
import java.util.Set;
|
|
|
import java.util.stream.Collectors;
|
|
|
+import java.util.Arrays;
|
|
|
|
|
|
+import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem;
|
|
|
import org.apache.hadoop.test.LambdaTestUtils;
|
|
|
+import org.apache.hadoop.util.Time;
|
|
|
import org.apache.hadoop.yarn.MockApps;
|
|
|
import org.apache.hadoop.yarn.api.protocolrecords.GetApplicationAttemptReportRequest;
|
|
|
import org.apache.hadoop.yarn.api.protocolrecords.GetApplicationAttemptReportResponse;
|
|
@@ -88,6 +91,14 @@ import org.apache.hadoop.yarn.api.protocolrecords.GetClusterNodeAttributesReques
|
|
|
import org.apache.hadoop.yarn.api.protocolrecords.GetClusterNodeAttributesResponse;
|
|
|
import org.apache.hadoop.yarn.api.protocolrecords.GetNodesToAttributesRequest;
|
|
|
import org.apache.hadoop.yarn.api.protocolrecords.GetNodesToAttributesResponse;
|
|
|
+import org.apache.hadoop.yarn.api.protocolrecords.GetNewReservationRequest;
|
|
|
+import org.apache.hadoop.yarn.api.protocolrecords.GetNewReservationResponse;
|
|
|
+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.api.protocolrecords.ReservationUpdateResponse;
|
|
|
+import org.apache.hadoop.yarn.api.protocolrecords.ReservationDeleteRequest;
|
|
|
+import org.apache.hadoop.yarn.api.protocolrecords.ReservationDeleteResponse;
|
|
|
import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
|
|
|
import org.apache.hadoop.yarn.api.records.ApplicationId;
|
|
|
import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext;
|
|
@@ -107,6 +118,10 @@ import org.apache.hadoop.yarn.api.records.NodeToAttributeValue;
|
|
|
import org.apache.hadoop.yarn.api.records.NodeAttribute;
|
|
|
import org.apache.hadoop.yarn.api.records.NodeAttributeInfo;
|
|
|
import org.apache.hadoop.yarn.api.records.NodeAttributeType;
|
|
|
+import org.apache.hadoop.yarn.api.records.ReservationRequest;
|
|
|
+import org.apache.hadoop.yarn.api.records.ReservationDefinition;
|
|
|
+import org.apache.hadoop.yarn.api.records.ReservationRequestInterpreter;
|
|
|
+import org.apache.hadoop.yarn.api.records.ReservationRequests;
|
|
|
import org.apache.hadoop.yarn.conf.YarnConfiguration;
|
|
|
import org.apache.hadoop.yarn.exceptions.YarnException;
|
|
|
import org.apache.hadoop.yarn.server.federation.policies.manager.UniformBroadcastPolicyManager;
|
|
@@ -117,6 +132,7 @@ import org.apache.hadoop.yarn.server.federation.utils.FederationStateStoreTestUt
|
|
|
import org.apache.hadoop.yarn.server.resourcemanager.MockRM;
|
|
|
import org.apache.hadoop.yarn.server.resourcemanager.MockNM;
|
|
|
import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager;
|
|
|
+import org.apache.hadoop.yarn.server.resourcemanager.reservation.ReservationSystem;
|
|
|
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMApp;
|
|
|
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMAppState;
|
|
|
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttemptState;
|
|
@@ -149,6 +165,8 @@ public class TestFederationClientInterceptor extends BaseRouterClientRMTest {
|
|
|
|
|
|
private final static int APP_PRIORITY_ZERO = 0;
|
|
|
|
|
|
+ private final static long DEFAULT_DURATION = 10 * 60 * 1000;
|
|
|
+
|
|
|
@Override
|
|
|
public void setUp() {
|
|
|
super.setUpConfig();
|
|
@@ -175,6 +193,7 @@ public class TestFederationClientInterceptor extends BaseRouterClientRMTest {
|
|
|
Assert.fail();
|
|
|
}
|
|
|
|
|
|
+ DefaultMetricsSystem.setMiniClusterMode(true);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -203,6 +222,12 @@ public class TestFederationClientInterceptor extends BaseRouterClientRMTest {
|
|
|
|
|
|
// Disable StateStoreFacade cache
|
|
|
conf.setInt(YarnConfiguration.FEDERATION_CACHE_TIME_TO_LIVE_SECS, 0);
|
|
|
+
|
|
|
+ conf.setInt("yarn.scheduler.minimum-allocation-mb", 512);
|
|
|
+ conf.setInt("yarn.scheduler.minimum-allocation-vcores", 1);
|
|
|
+ conf.setInt("yarn.scheduler.maximum-allocation-mb", 100 * 1024);
|
|
|
+ conf.setInt("yarn.scheduler.maximum-allocation-vcores", 100);
|
|
|
+
|
|
|
return conf;
|
|
|
}
|
|
|
|
|
@@ -1254,4 +1279,247 @@ public class TestFederationClientInterceptor extends BaseRouterClientRMTest {
|
|
|
NodeAttributeType.STRING, "nvida");
|
|
|
Assert.assertTrue(nodeAttributeMap.get("0-host1").contains(gpu));
|
|
|
}
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testGetNewReservation() throws Exception {
|
|
|
+ LOG.info("Test FederationClientInterceptor : Get NewReservation request.");
|
|
|
+
|
|
|
+ // null request
|
|
|
+ LambdaTestUtils.intercept(YarnException.class,
|
|
|
+ "Missing getNewReservation request.", () -> interceptor.getNewReservation(null));
|
|
|
+
|
|
|
+ // normal request
|
|
|
+ GetNewReservationRequest request = GetNewReservationRequest.newInstance();
|
|
|
+ GetNewReservationResponse response = interceptor.getNewReservation(request);
|
|
|
+ Assert.assertNotNull(response);
|
|
|
+
|
|
|
+ ReservationId reservationId = response.getReservationId();
|
|
|
+ Assert.assertNotNull(reservationId);
|
|
|
+ Assert.assertTrue(reservationId.toString().contains("reservation"));
|
|
|
+ Assert.assertEquals(reservationId.getClusterTimestamp(), ResourceManager.getClusterTimeStamp());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testSubmitReservation() throws Exception {
|
|
|
+ LOG.info("Test FederationClientInterceptor : SubmitReservation request.");
|
|
|
+
|
|
|
+ // get new reservationId
|
|
|
+ GetNewReservationRequest request = GetNewReservationRequest.newInstance();
|
|
|
+ GetNewReservationResponse response = interceptor.getNewReservation(request);
|
|
|
+ Assert.assertNotNull(response);
|
|
|
+
|
|
|
+ // allow plan follower to synchronize, manually trigger an assignment
|
|
|
+ Map<SubClusterId, MockRM> mockRMs = interceptor.getMockRMs();
|
|
|
+ for (MockRM mockRM : mockRMs.values()) {
|
|
|
+ ReservationSystem reservationSystem = mockRM.getReservationSystem();
|
|
|
+ reservationSystem.synchronizePlan("root.decided", true);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Submit Reservation
|
|
|
+ ReservationId reservationId = response.getReservationId();
|
|
|
+ ReservationDefinition rDefinition = createReservationDefinition(1024, 1);
|
|
|
+ ReservationSubmissionRequest rSubmissionRequest = ReservationSubmissionRequest.newInstance(
|
|
|
+ rDefinition, "decided", reservationId);
|
|
|
+
|
|
|
+ ReservationSubmissionResponse submissionResponse =
|
|
|
+ interceptor.submitReservation(rSubmissionRequest);
|
|
|
+ Assert.assertNotNull(submissionResponse);
|
|
|
+
|
|
|
+ SubClusterId subClusterId = stateStoreUtil.queryReservationHomeSC(reservationId);
|
|
|
+ Assert.assertNotNull(subClusterId);
|
|
|
+ Assert.assertTrue(subClusters.contains(subClusterId));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testSubmitReservationEmptyRequest() throws Exception {
|
|
|
+ LOG.info("Test FederationClientInterceptor : SubmitReservation request empty.");
|
|
|
+
|
|
|
+ String errorMsg =
|
|
|
+ "Missing submitReservation request or reservationId or reservation definition or queue.";
|
|
|
+
|
|
|
+ // null request1
|
|
|
+ LambdaTestUtils.intercept(YarnException.class, errorMsg,
|
|
|
+ () -> interceptor.submitReservation(null));
|
|
|
+
|
|
|
+ // null request2
|
|
|
+ ReservationSubmissionRequest request2 =
|
|
|
+ ReservationSubmissionRequest.newInstance(null, null, null);
|
|
|
+ LambdaTestUtils.intercept(YarnException.class, errorMsg,
|
|
|
+ () -> interceptor.submitReservation(request2));
|
|
|
+
|
|
|
+ // null request3
|
|
|
+ ReservationSubmissionRequest request3 =
|
|
|
+ ReservationSubmissionRequest.newInstance(null, "q1", null);
|
|
|
+ LambdaTestUtils.intercept(YarnException.class, errorMsg,
|
|
|
+ () -> interceptor.submitReservation(request3));
|
|
|
+
|
|
|
+ // null request4
|
|
|
+ ReservationId reservationId = ReservationId.newInstance(Time.now(), 1);
|
|
|
+ ReservationSubmissionRequest request4 =
|
|
|
+ ReservationSubmissionRequest.newInstance(null, null, reservationId);
|
|
|
+ LambdaTestUtils.intercept(YarnException.class, errorMsg,
|
|
|
+ () -> interceptor.submitReservation(request4));
|
|
|
+
|
|
|
+ // null request5
|
|
|
+ long arrival = Time.now();
|
|
|
+ long deadline = arrival + (int)(DEFAULT_DURATION * 1.1);
|
|
|
+
|
|
|
+ ReservationRequest rRequest = ReservationRequest.newInstance(
|
|
|
+ Resource.newInstance(1024, 1), 1, 1, DEFAULT_DURATION);
|
|
|
+ ReservationRequest[] rRequests = new ReservationRequest[] {rRequest};
|
|
|
+ ReservationDefinition rDefinition = createReservationDefinition(arrival, deadline, rRequests,
|
|
|
+ ReservationRequestInterpreter.R_ALL, "u1");
|
|
|
+ ReservationSubmissionRequest request5 =
|
|
|
+ ReservationSubmissionRequest.newInstance(rDefinition, null, reservationId);
|
|
|
+ LambdaTestUtils.intercept(YarnException.class, errorMsg,
|
|
|
+ () -> interceptor.submitReservation(request5));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testSubmitReservationMultipleSubmission() throws Exception {
|
|
|
+ LOG.info("Test FederationClientInterceptor: Submit Reservation - Multiple");
|
|
|
+
|
|
|
+ // get new reservationId
|
|
|
+ GetNewReservationRequest request = GetNewReservationRequest.newInstance();
|
|
|
+ GetNewReservationResponse response = interceptor.getNewReservation(request);
|
|
|
+ Assert.assertNotNull(response);
|
|
|
+
|
|
|
+ // allow plan follower to synchronize, manually trigger an assignment
|
|
|
+ Map<SubClusterId, MockRM> mockRMs = interceptor.getMockRMs();
|
|
|
+ for (MockRM mockRM : mockRMs.values()) {
|
|
|
+ ReservationSystem reservationSystem = mockRM.getReservationSystem();
|
|
|
+ reservationSystem.synchronizePlan("root.decided", true);
|
|
|
+ }
|
|
|
+
|
|
|
+ // First Submit Reservation
|
|
|
+ ReservationId reservationId = response.getReservationId();
|
|
|
+ ReservationDefinition rDefinition = createReservationDefinition(1024, 1);
|
|
|
+ ReservationSubmissionRequest rSubmissionRequest = ReservationSubmissionRequest.newInstance(
|
|
|
+ rDefinition, "decided", reservationId);
|
|
|
+ ReservationSubmissionResponse submissionResponse =
|
|
|
+ interceptor.submitReservation(rSubmissionRequest);
|
|
|
+ Assert.assertNotNull(submissionResponse);
|
|
|
+
|
|
|
+ SubClusterId subClusterId1 = stateStoreUtil.queryReservationHomeSC(reservationId);
|
|
|
+ Assert.assertNotNull(subClusterId1);
|
|
|
+ Assert.assertTrue(subClusters.contains(subClusterId1));
|
|
|
+
|
|
|
+ // First Retry
|
|
|
+ ReservationSubmissionResponse submissionResponse1 =
|
|
|
+ interceptor.submitReservation(rSubmissionRequest);
|
|
|
+ Assert.assertNotNull(submissionResponse1);
|
|
|
+ SubClusterId subClusterId2 = stateStoreUtil.queryReservationHomeSC(reservationId);
|
|
|
+ Assert.assertNotNull(subClusterId2);
|
|
|
+ Assert.assertEquals(subClusterId1, subClusterId2);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testUpdateReservation() throws Exception {
|
|
|
+ LOG.info("Test FederationClientInterceptor : UpdateReservation request.");
|
|
|
+
|
|
|
+ // get new reservationId
|
|
|
+ GetNewReservationRequest request = GetNewReservationRequest.newInstance();
|
|
|
+ GetNewReservationResponse response = interceptor.getNewReservation(request);
|
|
|
+ Assert.assertNotNull(response);
|
|
|
+
|
|
|
+ // allow plan follower to synchronize, manually trigger an assignment
|
|
|
+ Map<SubClusterId, MockRM> mockRMs = interceptor.getMockRMs();
|
|
|
+ for (MockRM mockRM : mockRMs.values()) {
|
|
|
+ ReservationSystem reservationSystem = mockRM.getReservationSystem();
|
|
|
+ reservationSystem.synchronizePlan("root.decided", true);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Submit Reservation
|
|
|
+ ReservationId reservationId = response.getReservationId();
|
|
|
+ ReservationDefinition rDefinition = createReservationDefinition(1024, 1);
|
|
|
+ ReservationSubmissionRequest rSubmissionRequest = ReservationSubmissionRequest.newInstance(
|
|
|
+ rDefinition, "decided", reservationId);
|
|
|
+
|
|
|
+ ReservationSubmissionResponse submissionResponse =
|
|
|
+ interceptor.submitReservation(rSubmissionRequest);
|
|
|
+ Assert.assertNotNull(submissionResponse);
|
|
|
+
|
|
|
+ // Update Reservation
|
|
|
+ ReservationDefinition rDefinition2 = createReservationDefinition(2048, 1);
|
|
|
+ ReservationUpdateRequest updateRequest =
|
|
|
+ ReservationUpdateRequest.newInstance(rDefinition2, reservationId);
|
|
|
+ ReservationUpdateResponse updateResponse =
|
|
|
+ interceptor.updateReservation(updateRequest);
|
|
|
+ Assert.assertNotNull(updateResponse);
|
|
|
+
|
|
|
+ SubClusterId subClusterId = stateStoreUtil.queryReservationHomeSC(reservationId);
|
|
|
+ Assert.assertNotNull(subClusterId);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testDeleteReservation() throws Exception {
|
|
|
+ LOG.info("Test FederationClientInterceptor : DeleteReservation request.");
|
|
|
+
|
|
|
+ // get new reservationId
|
|
|
+ GetNewReservationRequest request = GetNewReservationRequest.newInstance();
|
|
|
+ GetNewReservationResponse response = interceptor.getNewReservation(request);
|
|
|
+ Assert.assertNotNull(response);
|
|
|
+
|
|
|
+ // allow plan follower to synchronize, manually trigger an assignment
|
|
|
+ Map<SubClusterId, MockRM> mockRMs = interceptor.getMockRMs();
|
|
|
+ for (MockRM mockRM : mockRMs.values()) {
|
|
|
+ ReservationSystem reservationSystem = mockRM.getReservationSystem();
|
|
|
+ reservationSystem.synchronizePlan("root.decided", true);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Submit Reservation
|
|
|
+ ReservationId reservationId = response.getReservationId();
|
|
|
+ ReservationDefinition rDefinition = createReservationDefinition(1024, 1);
|
|
|
+ ReservationSubmissionRequest rSubmissionRequest = ReservationSubmissionRequest.newInstance(
|
|
|
+ rDefinition, "decided", reservationId);
|
|
|
+
|
|
|
+ ReservationSubmissionResponse submissionResponse =
|
|
|
+ interceptor.submitReservation(rSubmissionRequest);
|
|
|
+ Assert.assertNotNull(submissionResponse);
|
|
|
+
|
|
|
+ // Delete Reservation
|
|
|
+ ReservationDeleteRequest deleteRequest = ReservationDeleteRequest.newInstance(reservationId);
|
|
|
+ ReservationDeleteResponse deleteResponse = interceptor.deleteReservation(deleteRequest);
|
|
|
+ Assert.assertNotNull(deleteResponse);
|
|
|
+
|
|
|
+ LambdaTestUtils.intercept(YarnException.class,
|
|
|
+ "Reservation " + reservationId + " does not exist",
|
|
|
+ () -> stateStoreUtil.queryReservationHomeSC(reservationId));
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private ReservationDefinition createReservationDefinition(int memory, int core) {
|
|
|
+ // get reservationId
|
|
|
+ long arrival = Time.now();
|
|
|
+ long deadline = arrival + (int)(DEFAULT_DURATION * 1.1);
|
|
|
+
|
|
|
+ ReservationRequest rRequest = ReservationRequest.newInstance(
|
|
|
+ Resource.newInstance(memory, core), 1, 1, DEFAULT_DURATION);
|
|
|
+ ReservationRequest[] rRequests = new ReservationRequest[] {rRequest};
|
|
|
+
|
|
|
+ ReservationDefinition rDefinition = createReservationDefinition(arrival, deadline, rRequests,
|
|
|
+ ReservationRequestInterpreter.R_ALL, "u1");
|
|
|
+
|
|
|
+ return rDefinition;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This method is used to create a ReservationDefinition.
|
|
|
+ *
|
|
|
+ * @param arrival Job arrival time
|
|
|
+ * @param deadline Job deadline
|
|
|
+ * @param reservationRequests reservationRequest Array
|
|
|
+ * @param rType Enumeration of various types of
|
|
|
+ * dependencies among multiple ReservationRequest
|
|
|
+ * @param username username
|
|
|
+ * @return ReservationDefinition
|
|
|
+ */
|
|
|
+ private ReservationDefinition createReservationDefinition(long arrival,
|
|
|
+ long deadline, ReservationRequest[] reservationRequests,
|
|
|
+ ReservationRequestInterpreter rType, String username) {
|
|
|
+ ReservationRequests requests = ReservationRequests
|
|
|
+ .newInstance(Arrays.asList(reservationRequests), rType);
|
|
|
+ return ReservationDefinition.newInstance(arrival, deadline,
|
|
|
+ requests, username, "0", Priority.UNDEFINED);
|
|
|
+ }
|
|
|
}
|