1 /*******************************************************************************
2  *   Licensed to the Apache Software Foundation (ASF) under one
3  *   or more contributor license agreements.  See the NOTICE file
4  *   distributed with this work for additional information
5  *   regarding copyright ownership.  The ASF licenses this file
6  *   to you under the Apache License, Version 2.0 (the
7  *   "License"); you may not use this file except in compliance
8  *   with the License.  You may obtain a copy of the License at
9  *
10  *       http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *   Unless required by applicable law or agreed to in writing, software
13  *   distributed under the License is distributed on an "AS IS" BASIS,
14  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *   See the License for the specific language governing permissions and
16  *   limitations under the License.
17  *******************************************************************************/
18 package org.apache.hadoop.yarn.server.resourcemanager.reservation;
19 
20 import static org.mockito.Matchers.any;
21 import static org.mockito.Mockito.mock;
22 import static org.mockito.Mockito.when;
23 
24 import java.text.MessageFormat;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.Map;
28 
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.hadoop.yarn.api.protocolrecords.ReservationDeleteRequest;
32 import org.apache.hadoop.yarn.api.protocolrecords.ReservationSubmissionRequest;
33 import org.apache.hadoop.yarn.api.protocolrecords.ReservationUpdateRequest;
34 import org.apache.hadoop.yarn.api.protocolrecords.impl.pb.ReservationDeleteRequestPBImpl;
35 import org.apache.hadoop.yarn.api.protocolrecords.impl.pb.ReservationSubmissionRequestPBImpl;
36 import org.apache.hadoop.yarn.api.protocolrecords.impl.pb.ReservationUpdateRequestPBImpl;
37 import org.apache.hadoop.yarn.api.records.ReservationDefinition;
38 import org.apache.hadoop.yarn.api.records.ReservationId;
39 import org.apache.hadoop.yarn.api.records.ReservationRequest;
40 import org.apache.hadoop.yarn.api.records.ReservationRequestInterpreter;
41 import org.apache.hadoop.yarn.api.records.ReservationRequests;
42 import org.apache.hadoop.yarn.api.records.Resource;
43 import org.apache.hadoop.yarn.api.records.impl.pb.ReservationDefinitionPBImpl;
44 import org.apache.hadoop.yarn.api.records.impl.pb.ReservationRequestsPBImpl;
45 import org.apache.hadoop.yarn.exceptions.YarnException;
46 import org.apache.hadoop.yarn.util.Clock;
47 import org.apache.hadoop.yarn.util.resource.DefaultResourceCalculator;
48 import org.apache.hadoop.yarn.util.resource.ResourceCalculator;
49 import org.junit.After;
50 import org.junit.Assert;
51 import org.junit.Before;
52 import org.junit.Test;
53 
54 public class TestReservationInputValidator {
55 
56   private static final Log LOG = LogFactory
57       .getLog(TestReservationInputValidator.class);
58 
59   private static final String PLAN_NAME = "test-reservation";
60 
61   private Clock clock;
62   private Map<String, Plan> plans = new HashMap<String, Plan>(1);
63   private ReservationSystem rSystem;
64   private Plan plan;
65 
66   private ReservationInputValidator rrValidator;
67 
68   @Before
setUp()69   public void setUp() {
70     clock = mock(Clock.class);
71     plan = mock(Plan.class);
72     rSystem = mock(ReservationSystem.class);
73     plans.put(PLAN_NAME, plan);
74     rrValidator = new ReservationInputValidator(clock);
75     when(clock.getTime()).thenReturn(1L);
76     ResourceCalculator rCalc = new DefaultResourceCalculator();
77     Resource resource = Resource.newInstance(10240, 10);
78     when(plan.getResourceCalculator()).thenReturn(rCalc);
79     when(plan.getTotalCapacity()).thenReturn(resource);
80     when(rSystem.getQueueForReservation(any(ReservationId.class))).thenReturn(
81         PLAN_NAME);
82     when(rSystem.getPlan(PLAN_NAME)).thenReturn(plan);
83   }
84 
85   @After
tearDown()86   public void tearDown() {
87     rrValidator = null;
88     clock = null;
89     plan = null;
90   }
91 
92   @Test
testSubmitReservationNormal()93   public void testSubmitReservationNormal() {
94     ReservationSubmissionRequest request =
95         createSimpleReservationSubmissionRequest(1, 1, 1, 5, 3);
96     Plan plan = null;
97     try {
98       plan =
99           rrValidator.validateReservationSubmissionRequest(rSystem, request,
100               ReservationSystemTestUtil.getNewReservationId());
101     } catch (YarnException e) {
102       Assert.fail(e.getMessage());
103     }
104     Assert.assertNotNull(plan);
105   }
106 
107   @Test
testSubmitReservationDoesnotExist()108   public void testSubmitReservationDoesnotExist() {
109     ReservationSubmissionRequest request =
110         new ReservationSubmissionRequestPBImpl();
111     Plan plan = null;
112     try {
113       plan =
114           rrValidator.validateReservationSubmissionRequest(rSystem, request,
115               ReservationSystemTestUtil.getNewReservationId());
116       Assert.fail();
117     } catch (YarnException e) {
118       Assert.assertNull(plan);
119       String message = e.getMessage();
120       Assert
121           .assertTrue(message
122               .equals("The queue to submit is not specified. Please try again with a valid reservable queue."));
123       LOG.info(message);
124     }
125   }
126 
127   @Test
testSubmitReservationInvalidPlan()128   public void testSubmitReservationInvalidPlan() {
129     ReservationSubmissionRequest request =
130         createSimpleReservationSubmissionRequest(1, 1, 1, 5, 3);
131     when(rSystem.getPlan(PLAN_NAME)).thenReturn(null);
132     Plan plan = null;
133     try {
134       plan =
135           rrValidator.validateReservationSubmissionRequest(rSystem, request,
136               ReservationSystemTestUtil.getNewReservationId());
137       Assert.fail();
138     } catch (YarnException e) {
139       Assert.assertNull(plan);
140       String message = e.getMessage();
141       Assert
142           .assertTrue(message
143               .endsWith(" is not managed by reservation system. Please try again with a valid reservable queue."));
144       LOG.info(message);
145     }
146   }
147 
148   @Test
testSubmitReservationNoDefinition()149   public void testSubmitReservationNoDefinition() {
150     ReservationSubmissionRequest request =
151         new ReservationSubmissionRequestPBImpl();
152     request.setQueue(PLAN_NAME);
153     Plan plan = null;
154     try {
155       plan =
156           rrValidator.validateReservationSubmissionRequest(rSystem, request,
157               ReservationSystemTestUtil.getNewReservationId());
158       Assert.fail();
159     } catch (YarnException e) {
160       Assert.assertNull(plan);
161       String message = e.getMessage();
162       Assert
163           .assertTrue(message
164               .equals("Missing reservation definition. Please try again by specifying a reservation definition."));
165       LOG.info(message);
166     }
167   }
168 
169   @Test
testSubmitReservationInvalidDeadline()170   public void testSubmitReservationInvalidDeadline() {
171     ReservationSubmissionRequest request =
172         createSimpleReservationSubmissionRequest(1, 1, 1, 0, 3);
173     Plan plan = null;
174     try {
175       plan =
176           rrValidator.validateReservationSubmissionRequest(rSystem, request,
177               ReservationSystemTestUtil.getNewReservationId());
178       Assert.fail();
179     } catch (YarnException e) {
180       Assert.assertNull(plan);
181       String message = e.getMessage();
182       Assert.assertTrue(message
183           .startsWith("The specified deadline: 0 is the past"));
184       LOG.info(message);
185     }
186   }
187 
188   @Test
testSubmitReservationInvalidRR()189   public void testSubmitReservationInvalidRR() {
190     ReservationSubmissionRequest request =
191         createSimpleReservationSubmissionRequest(0, 0, 1, 5, 3);
192     Plan plan = null;
193     try {
194       plan =
195           rrValidator.validateReservationSubmissionRequest(rSystem, request,
196               ReservationSystemTestUtil.getNewReservationId());
197       Assert.fail();
198     } catch (YarnException e) {
199       Assert.assertNull(plan);
200       String message = e.getMessage();
201       Assert.assertTrue(message
202           .startsWith("No resources have been specified to reserve"));
203       LOG.info(message);
204     }
205   }
206 
207   @Test
testSubmitReservationEmptyRR()208   public void testSubmitReservationEmptyRR() {
209     ReservationSubmissionRequest request =
210         createSimpleReservationSubmissionRequest(1, 0, 1, 5, 3);
211     Plan plan = null;
212     try {
213       plan =
214           rrValidator.validateReservationSubmissionRequest(rSystem, request,
215               ReservationSystemTestUtil.getNewReservationId());
216       Assert.fail();
217     } catch (YarnException e) {
218       Assert.assertNull(plan);
219       String message = e.getMessage();
220       Assert.assertTrue(message
221           .startsWith("No resources have been specified to reserve"));
222       LOG.info(message);
223     }
224   }
225 
226   @Test
testSubmitReservationInvalidDuration()227   public void testSubmitReservationInvalidDuration() {
228     ReservationSubmissionRequest request =
229         createSimpleReservationSubmissionRequest(1, 1, 1, 3, 4);
230     Plan plan = null;
231     try {
232       plan =
233           rrValidator.validateReservationSubmissionRequest(rSystem, request,
234               ReservationSystemTestUtil.getNewReservationId());
235       Assert.fail();
236     } catch (YarnException e) {
237       Assert.assertNull(plan);
238       String message = e.getMessage();
239       Assert.assertTrue(message.startsWith("The time difference"));
240       Assert
241           .assertTrue(message
242               .contains("must  be greater or equal to the minimum resource duration"));
243       LOG.info(message);
244     }
245   }
246 
247   @Test
testSubmitReservationExceedsGangSize()248   public void testSubmitReservationExceedsGangSize() {
249     ReservationSubmissionRequest request =
250         createSimpleReservationSubmissionRequest(1, 1, 1, 5, 4);
251     Resource resource = Resource.newInstance(512, 1);
252     when(plan.getTotalCapacity()).thenReturn(resource);
253     Plan plan = null;
254     try {
255       plan =
256           rrValidator.validateReservationSubmissionRequest(rSystem, request,
257               ReservationSystemTestUtil.getNewReservationId());
258       Assert.fail();
259     } catch (YarnException e) {
260       Assert.assertNull(plan);
261       String message = e.getMessage();
262       Assert
263           .assertTrue(message
264               .startsWith("The size of the largest gang in the reservation refinition"));
265       Assert.assertTrue(message.contains("exceed the capacity available "));
266       LOG.info(message);
267     }
268   }
269 
270   @Test
testUpdateReservationNormal()271   public void testUpdateReservationNormal() {
272     ReservationUpdateRequest request =
273         createSimpleReservationUpdateRequest(1, 1, 1, 5, 3);
274     Plan plan = null;
275     try {
276       plan = rrValidator.validateReservationUpdateRequest(rSystem, request);
277     } catch (YarnException e) {
278       Assert.fail(e.getMessage());
279     }
280     Assert.assertNotNull(plan);
281   }
282 
283   @Test
testUpdateReservationNoID()284   public void testUpdateReservationNoID() {
285     ReservationUpdateRequest request = new ReservationUpdateRequestPBImpl();
286     Plan plan = null;
287     try {
288       plan = rrValidator.validateReservationUpdateRequest(rSystem, request);
289       Assert.fail();
290     } catch (YarnException e) {
291       Assert.assertNull(plan);
292       String message = e.getMessage();
293       Assert
294           .assertTrue(message
295               .startsWith("Missing reservation id. Please try again by specifying a reservation id."));
296       LOG.info(message);
297     }
298   }
299 
300   @Test
testUpdateReservationDoesnotExist()301   public void testUpdateReservationDoesnotExist() {
302     ReservationUpdateRequest request =
303         createSimpleReservationUpdateRequest(1, 1, 1, 5, 4);
304     ReservationId rId = request.getReservationId();
305     when(rSystem.getQueueForReservation(rId)).thenReturn(null);
306     Plan plan = null;
307     try {
308       plan = rrValidator.validateReservationUpdateRequest(rSystem, request);
309       Assert.fail();
310     } catch (YarnException e) {
311       Assert.assertNull(plan);
312       String message = e.getMessage();
313       Assert
314           .assertTrue(message.equals(MessageFormat
315               .format(
316                   "The specified reservation with ID: {0} is unknown. Please try again with a valid reservation.",
317                   rId)));
318       LOG.info(message);
319     }
320   }
321 
322   @Test
testUpdateReservationInvalidPlan()323   public void testUpdateReservationInvalidPlan() {
324     ReservationUpdateRequest request =
325         createSimpleReservationUpdateRequest(1, 1, 1, 5, 4);
326     when(rSystem.getPlan(PLAN_NAME)).thenReturn(null);
327     Plan plan = null;
328     try {
329       plan = rrValidator.validateReservationUpdateRequest(rSystem, request);
330       Assert.fail();
331     } catch (YarnException e) {
332       Assert.assertNull(plan);
333       String message = e.getMessage();
334       Assert
335           .assertTrue(message
336               .endsWith(" is not associated with any valid plan. Please try again with a valid reservation."));
337       LOG.info(message);
338     }
339   }
340 
341   @Test
testUpdateReservationNoDefinition()342   public void testUpdateReservationNoDefinition() {
343     ReservationUpdateRequest request = new ReservationUpdateRequestPBImpl();
344     request.setReservationId(ReservationSystemTestUtil.getNewReservationId());
345     Plan plan = null;
346     try {
347       plan = rrValidator.validateReservationUpdateRequest(rSystem, request);
348       Assert.fail();
349     } catch (YarnException e) {
350       Assert.assertNull(plan);
351       String message = e.getMessage();
352       Assert
353           .assertTrue(message
354               .startsWith("Missing reservation definition. Please try again by specifying a reservation definition."));
355       LOG.info(message);
356     }
357   }
358 
359   @Test
testUpdateReservationInvalidDeadline()360   public void testUpdateReservationInvalidDeadline() {
361     ReservationUpdateRequest request =
362         createSimpleReservationUpdateRequest(1, 1, 1, 0, 3);
363     Plan plan = null;
364     try {
365       plan = rrValidator.validateReservationUpdateRequest(rSystem, request);
366       Assert.fail();
367     } catch (YarnException e) {
368       Assert.assertNull(plan);
369       String message = e.getMessage();
370       Assert.assertTrue(message
371           .startsWith("The specified deadline: 0 is the past"));
372       LOG.info(message);
373     }
374   }
375 
376   @Test
testUpdateReservationInvalidRR()377   public void testUpdateReservationInvalidRR() {
378     ReservationUpdateRequest request =
379         createSimpleReservationUpdateRequest(0, 0, 1, 5, 3);
380     Plan plan = null;
381     try {
382       plan = rrValidator.validateReservationUpdateRequest(rSystem, request);
383       Assert.fail();
384     } catch (YarnException e) {
385       Assert.assertNull(plan);
386       String message = e.getMessage();
387       Assert.assertTrue(message
388           .startsWith("No resources have been specified to reserve"));
389       LOG.info(message);
390     }
391   }
392 
393   @Test
testUpdateReservationEmptyRR()394   public void testUpdateReservationEmptyRR() {
395     ReservationUpdateRequest request =
396         createSimpleReservationUpdateRequest(1, 0, 1, 5, 3);
397     Plan plan = null;
398     try {
399       plan = rrValidator.validateReservationUpdateRequest(rSystem, request);
400       Assert.fail();
401     } catch (YarnException e) {
402       Assert.assertNull(plan);
403       String message = e.getMessage();
404       Assert.assertTrue(message
405           .startsWith("No resources have been specified to reserve"));
406       LOG.info(message);
407     }
408   }
409 
410   @Test
testUpdateReservationInvalidDuration()411   public void testUpdateReservationInvalidDuration() {
412     ReservationUpdateRequest request =
413         createSimpleReservationUpdateRequest(1, 1, 1, 3, 4);
414     Plan plan = null;
415     try {
416       plan = rrValidator.validateReservationUpdateRequest(rSystem, request);
417       Assert.fail();
418     } catch (YarnException e) {
419       Assert.assertNull(plan);
420       String message = e.getMessage();
421       Assert
422           .assertTrue(message
423               .contains("must  be greater or equal to the minimum resource duration"));
424       LOG.info(message);
425     }
426   }
427 
428   @Test
testUpdateReservationExceedsGangSize()429   public void testUpdateReservationExceedsGangSize() {
430     ReservationUpdateRequest request =
431         createSimpleReservationUpdateRequest(1, 1, 1, 5, 4);
432     Resource resource = Resource.newInstance(512, 1);
433     when(plan.getTotalCapacity()).thenReturn(resource);
434     Plan plan = null;
435     try {
436       plan = rrValidator.validateReservationUpdateRequest(rSystem, request);
437       Assert.fail();
438     } catch (YarnException e) {
439       Assert.assertNull(plan);
440       String message = e.getMessage();
441       Assert
442           .assertTrue(message
443               .startsWith("The size of the largest gang in the reservation refinition"));
444       Assert.assertTrue(message.contains("exceed the capacity available "));
445       LOG.info(message);
446     }
447   }
448 
449   @Test
testDeleteReservationNormal()450   public void testDeleteReservationNormal() {
451     ReservationDeleteRequest request = new ReservationDeleteRequestPBImpl();
452     ReservationId reservationID =
453         ReservationSystemTestUtil.getNewReservationId();
454     request.setReservationId(reservationID);
455     ReservationAllocation reservation = mock(ReservationAllocation.class);
456     when(plan.getReservationById(reservationID)).thenReturn(reservation);
457     Plan plan = null;
458     try {
459       plan = rrValidator.validateReservationDeleteRequest(rSystem, request);
460     } catch (YarnException e) {
461       Assert.fail(e.getMessage());
462     }
463     Assert.assertNotNull(plan);
464   }
465 
466   @Test
testDeleteReservationNoID()467   public void testDeleteReservationNoID() {
468     ReservationDeleteRequest request = new ReservationDeleteRequestPBImpl();
469     Plan plan = null;
470     try {
471       plan = rrValidator.validateReservationDeleteRequest(rSystem, request);
472       Assert.fail();
473     } catch (YarnException e) {
474       Assert.assertNull(plan);
475       String message = e.getMessage();
476       Assert
477           .assertTrue(message
478               .startsWith("Missing reservation id. Please try again by specifying a reservation id."));
479       LOG.info(message);
480     }
481   }
482 
483   @Test
testDeleteReservationDoesnotExist()484   public void testDeleteReservationDoesnotExist() {
485     ReservationDeleteRequest request = new ReservationDeleteRequestPBImpl();
486     ReservationId rId = ReservationSystemTestUtil.getNewReservationId();
487     request.setReservationId(rId);
488     when(rSystem.getQueueForReservation(rId)).thenReturn(null);
489     Plan plan = null;
490     try {
491       plan = rrValidator.validateReservationDeleteRequest(rSystem, request);
492       Assert.fail();
493     } catch (YarnException e) {
494       Assert.assertNull(plan);
495       String message = e.getMessage();
496       Assert
497           .assertTrue(message.equals(MessageFormat
498               .format(
499                   "The specified reservation with ID: {0} is unknown. Please try again with a valid reservation.",
500                   rId)));
501       LOG.info(message);
502     }
503   }
504 
505   @Test
testDeleteReservationInvalidPlan()506   public void testDeleteReservationInvalidPlan() {
507     ReservationDeleteRequest request = new ReservationDeleteRequestPBImpl();
508     ReservationId reservationID =
509         ReservationSystemTestUtil.getNewReservationId();
510     request.setReservationId(reservationID);
511     when(rSystem.getPlan(PLAN_NAME)).thenReturn(null);
512     Plan plan = null;
513     try {
514       plan = rrValidator.validateReservationDeleteRequest(rSystem, request);
515       Assert.fail();
516     } catch (YarnException e) {
517       Assert.assertNull(plan);
518       String message = e.getMessage();
519       Assert
520           .assertTrue(message
521               .endsWith(" is not associated with any valid plan. Please try again with a valid reservation."));
522       LOG.info(message);
523     }
524   }
525 
createSimpleReservationSubmissionRequest( int numRequests, int numContainers, long arrival, long deadline, long duration)526   private ReservationSubmissionRequest createSimpleReservationSubmissionRequest(
527       int numRequests, int numContainers, long arrival, long deadline,
528       long duration) {
529     // create a request with a single atomic ask
530     ReservationSubmissionRequest request =
531         new ReservationSubmissionRequestPBImpl();
532     ReservationDefinition rDef = new ReservationDefinitionPBImpl();
533     rDef.setArrival(arrival);
534     rDef.setDeadline(deadline);
535     if (numRequests > 0) {
536       ReservationRequests reqs = new ReservationRequestsPBImpl();
537       rDef.setReservationRequests(reqs);
538       if (numContainers > 0) {
539         ReservationRequest r =
540             ReservationRequest.newInstance(Resource.newInstance(1024, 1),
541                 numContainers, 1, duration);
542 
543         reqs.setReservationResources(Collections.singletonList(r));
544         reqs.setInterpreter(ReservationRequestInterpreter.R_ALL);
545       }
546     }
547     request.setQueue(PLAN_NAME);
548     request.setReservationDefinition(rDef);
549     return request;
550   }
551 
createSimpleReservationUpdateRequest( int numRequests, int numContainers, long arrival, long deadline, long duration)552   private ReservationUpdateRequest createSimpleReservationUpdateRequest(
553       int numRequests, int numContainers, long arrival, long deadline,
554       long duration) {
555     // create a request with a single atomic ask
556     ReservationUpdateRequest request = new ReservationUpdateRequestPBImpl();
557     ReservationDefinition rDef = new ReservationDefinitionPBImpl();
558     rDef.setArrival(arrival);
559     rDef.setDeadline(deadline);
560     if (numRequests > 0) {
561       ReservationRequests reqs = new ReservationRequestsPBImpl();
562       rDef.setReservationRequests(reqs);
563       if (numContainers > 0) {
564         ReservationRequest r =
565             ReservationRequest.newInstance(Resource.newInstance(1024, 1),
566                 numContainers, 1, duration);
567 
568         reqs.setReservationResources(Collections.singletonList(r));
569         reqs.setInterpreter(ReservationRequestInterpreter.R_ALL);
570       }
571     }
572     request.setReservationDefinition(rDef);
573     request.setReservationId(ReservationSystemTestUtil.getNewReservationId());
574     return request;
575   }
576 
577 }
578