YARN-10535. Make queue placement in CapacityScheduler compliant with auto-queue-placement. Contributed by Gergely Pollak

This commit is contained in:
Szilard Nemeth 2021-01-18 20:19:36 +01:00
parent 9b4f09a6bb
commit 6abdb148e4
7 changed files with 436 additions and 147 deletions

View File

@ -26,7 +26,12 @@
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.exceptions.YarnException;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.ResourceScheduler;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.*;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CSQueue;
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.capacity.CapacitySchedulerContext;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacitySchedulerQueueManager;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.LeafQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -250,46 +255,47 @@ private String validateAndNormalizeQueue(
private String validateAndNormalizeQueueWithParent(
String parent, String leaf, boolean allowCreate) throws YarnException {
CSQueue parentQueue = queueManager.getQueue(parent);
//we don't find the specified parent, so the placement rule is invalid
//for this case
if (parentQueue == null) {
if (queueManager.isAmbiguous(parent)) {
throw new YarnException("Mapping rule specified a parent queue '" +
parent + "', but it is ambiguous.");
} else {
throw new YarnException("Mapping rule specified a parent queue '" +
parent + "', but it does not exist.");
}
}
String normalizedPath =
MappingRuleValidationHelper.normalizeQueuePathRoot(
queueManager, parent + DOT + leaf);
MappingRuleValidationHelper.ValidationResult validity =
MappingRuleValidationHelper.validateQueuePathAutoCreation(
queueManager, normalizedPath);
//normalizing parent path
String parentPath = parentQueue.getQueuePath();
String fullPath = parentPath + DOT + leaf;
//checking if the queue actually exists
CSQueue queue = queueManager.getQueue(fullPath);
//if we have a parent which is not a managed parent and the queue doesn't
//then it is an invalid target, since the queue won't be auto-created
if (!(parentQueue instanceof ManagedParentQueue) && queue == null) {
switch (validity) {
case AMBIGUOUS_PARENT:
throw new YarnException("Mapping rule specified a parent queue '" +
parent + "', but it is not a managed parent queue, " +
parent + "', but it is ambiguous.");
case AMBIGUOUS_QUEUE:
throw new YarnException("Mapping rule specified a target queue '" +
normalizedPath + "', but it is ambiguous.");
case EMPTY_PATH:
throw new YarnException("Mapping rule did not specify a target queue.");
case NO_PARENT_PROVIDED:
throw new YarnException("Mapping rule did not specify an existing queue" +
" nor a dynamic parent queue.");
case NO_DYNAMIC_PARENT:
throw new YarnException("Mapping rule specified a parent queue '" +
parent + "', but it is not a dynamic parent queue, " +
"and no queue exists with name '" + leaf + "' under it.");
case QUEUE_EXISTS:
break;
case CREATABLE:
if (!allowCreate) {
throw new YarnException("Mapping rule doesn't allow auto-creation of " +
"the queue '" + normalizedPath + "'.");
}
break;
default:
//Probably the QueueCreationValidation have
//new items, which are not handled here
throw new YarnException("Unknown queue path validation result. '" +
validity + "'.");
}
//if the queue does not exist but the parent is managed we need to check if
//auto-creation is allowed
if (parentQueue instanceof ManagedParentQueue
&& queue == null
&& allowCreate == false) {
throw new YarnException("Mapping rule doesn't allow auto-creation of " +
"the queue '" + fullPath + "'");
}
//at this point we either have a managed parent or the queue actually
//exists so we have a placement context, returning it
return fullPath;
//at this point we either have a dynamic parent or the queue actually
//exists, returning it
return normalizedPath;
}
private String validateAndNormalizeQueueWithNoParent(String leaf)

View File

@ -46,8 +46,9 @@ interface MappingRuleValidationContext {
* A part is dynamic if a known variable is referenced in it.
* @param queuePath The path to check
* @return true if no dynamic parts were found
* @throws YarnException if invalid path parts are found (eg. empty)
*/
boolean isPathStatic(String queuePath);
boolean isPathStatic(String queuePath) throws YarnException;
/**
* This method will add a known variable to the validation context, known

View File

@ -24,8 +24,11 @@
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacitySchedulerQueueManager;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.LeafQueue;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.ManagedParentQueue;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.ParentQueue;
import java.util.Set;
import java.util.*;
import static org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacitySchedulerConfiguration.DOT;
public class MappingRuleValidationContextImpl
implements MappingRuleValidationContext {
@ -51,76 +54,56 @@ public class MappingRuleValidationContextImpl
/**
* This method will determine if a static queue path is valid.
* We consider a path static (in the target path validation context)
* If non if it's parts contain any substitutable variables.
* eg. root.groups.bob is static, while root.groups.%user is dynamic
* @param path The static path of the queue
* @return true of the path is valid
* @return true if the path is valid
* @throws YarnException if the path is invalid
*/
private boolean validateStaticQueuePath(MappingQueuePath path)
throws YarnException {
//Try getting queue by its full path name, if it exists it is a static
//leaf queue indeed, without any auto creation magic
CSQueue queue = queueManager.getQueue(path.getFullPath());
if (queue == null) {
//We might not be able to find the queue, because the reference was
// ambiguous this should only happen if the queue was referenced by
// leaf name only
if (queueManager.isAmbiguous(path.getFullPath())) {
throw new YarnException(
"Target queue is an ambiguous leaf queue '" +
path.getFullPath() + "'");
}
String normalizedPath = MappingRuleValidationHelper.normalizeQueuePathRoot(
queueManager, path.getFullPath());
MappingRuleValidationHelper.ValidationResult validity =
MappingRuleValidationHelper.validateQueuePathAutoCreation(
queueManager, normalizedPath);
//if leaf queue does not exist,
//we need to check if the parent exists and is a managed parent
if (!path.hasParent()) {
throw new YarnException(
"Target queue does not exist and has no parent defined '" +
path.getFullPath() + "'");
}
CSQueue parentQueue = queueManager.getQueue(path.getParent());
if (parentQueue == null) {
if (queueManager.isAmbiguous(path.getParent())) {
throw new YarnException("Target queue path '" + path +
"' contains an ambiguous parent queue '" +
path.getParent() + "' reference");
} else {
throw new YarnException("Target queue path '" + path + "' " +
"contains an invalid parent queue '" + path.getParent() + "'.");
}
}
if (!(parentQueue instanceof ManagedParentQueue)) {
//If the parent path was referenced by short name, and it is not
// managed, we look up if there is a queue under it with the leaf
// queue's name
String normalizedParentPath = parentQueue.getQueuePath() + "."
+ path.getLeafName();
CSQueue normalizedQueue = queueManager.getQueue(normalizedParentPath);
if (normalizedQueue instanceof LeafQueue) {
return true;
}
if (normalizedQueue == null) {
throw new YarnException(
"Target queue '" + path.getFullPath() + "' does not exist" +
" and has a non-managed parent queue defined.");
} else {
throw new YarnException("Target queue '" + path + "' references" +
"a non-leaf queue, target queues must always be " +
"leaf queues.");
}
}
} else {
// if queue exists, validate if its an instance of leaf queue
switch (validity) {
case AMBIGUOUS_PARENT:
throw new YarnException("Target queue path '" + path +
"' contains an ambiguous parent queue '" +
path.getParent() + "' reference.");
case AMBIGUOUS_QUEUE:
throw new YarnException("Target queue is an ambiguous leaf queue '" +
path.getFullPath() + "'.");
case EMPTY_PATH:
throw new YarnException("Mapping rule did not specify a target queue.");
case NO_PARENT_PROVIDED:
throw new YarnException(
"Target queue does not exist and has no parent defined '" +
path.getFullPath() + "'.");
case NO_DYNAMIC_PARENT:
throw new YarnException("Mapping rule specified a parent queue '" +
path.getParent() + "', but it is not a dynamic parent queue, " +
"and no queue exists with name '" + path.getLeafName() +
"' under it.");
case QUEUE_EXISTS:
CSQueue queue = queueManager.getQueue(normalizedPath);
if (!(queue instanceof LeafQueue)) {
throw new YarnException("Target queue '" + path + "' references" +
"a non-leaf queue, target queues must always be " +
"leaf queues.");
throw new YarnException("Target queue '" + path.getFullPath() +
"' but it's not a leaf queue.");
}
break;
case CREATABLE:
break;
default:
//Probably the QueueCreationValidation have
//new items, which are not handled here
throw new YarnException("Unknown queue path validation result. '" +
validity + "'.");
}
return true;
}
@ -133,47 +116,91 @@ private boolean validateStaticQueuePath(MappingQueuePath path)
*/
private boolean validateDynamicQueuePath(MappingQueuePath path)
throws YarnException{
//if the queue is dynamic and we don't have a parent path, we cannot do
//any validation, since the dynamic part can be substituted to anything
//and that is the only part
if (!path.hasParent()) {
ArrayList<String> parts = new ArrayList<>();
Collections.addAll(parts, path.getFullPath().split("\\."));
//How deep is the path to be created after the root element
Iterator<String> pointer = parts.iterator();
if (!pointer.hasNext()) {
//This should not happen since we only call validateDynamicQueuePath
//if we have found at least ONE dynamic part, which implies the path is
//not empty, so if we get here, I'm really curious what the path was,
//that's the reason we give back a theoretically "empty" path
throw new YarnException("Empty queue path provided '" + path + "'");
}
StringBuilder staticPartBuffer = new StringBuilder(pointer.next());
String staticPartParent = null;
//If not even the root of the reference is static we cannot validate
if (!isPathStatic(staticPartBuffer.toString())) {
return true;
}
String parent = path.getParent();
//if the parent path has dynamic parts, we cannot do any more validations
if (!isPathStatic(parent)) {
return true;
}
//We check if the parent queue exists
CSQueue parentQueue = queueManager.getQueue(parent);
if (parentQueue == null) {
throw new YarnException("Target queue path '" + path + "' contains an " +
"invalid parent queue");
}
if (!(parentQueue instanceof ManagedParentQueue)) {
if (parentQueue.getChildQueues() != null) {
for (CSQueue queue : parentQueue.getChildQueues()) {
if (queue instanceof LeafQueue) {
//if a non managed parent queue has at least one leaf queue, this
//mapping can be valid, we cannot do any more checks
return true;
}
}
//getting the static part of the queue, we can only validate that
while (pointer.hasNext()) {
String nextPart = pointer.next();
if (isPathStatic(nextPart)) {
staticPartParent = staticPartBuffer.toString();
staticPartBuffer.append(DOT).append(nextPart);
} else {
//when we find the first dynamic part, we stop the search
break;
}
}
String staticPart = staticPartBuffer.toString();
//There is no way we can place anything into the queue referenced by the
// rule, because we cannot auto create, and we don't have any leaf queues
//Actually this branch is not accessible with the current queue hierarchy,
//there should be no parents without any leaf queues. This condition says
//for sanity checks
throw new YarnException("Target queue path '" + path + "' has " +
"a non-managed parent queue which has no LeafQueues either.");
String normalizedStaticPart =
MappingRuleValidationHelper.normalizeQueuePathRoot(
queueManager, staticPart);
CSQueue queue = queueManager.getQueue(normalizedStaticPart);
//if the static part of our queue exists, and it's not a leaf queue,
//we cannot do any deeper validation
if (queue != null) {
if (queue instanceof LeafQueue) {
throw new YarnException("Queue path '" + path +"' is invalid " +
"because '" + normalizedStaticPart + "' is a leaf queue, " +
"which can have no other queues under it.");
}
return true;
}
return true;
if (staticPartParent != null) {
String normalizedStaticPartParent
= MappingRuleValidationHelper.normalizeQueuePathRoot(
queueManager, staticPartParent);
queue = queueManager.getQueue(normalizedStaticPartParent);
//if the parent of our static part is eligible for creation, we validate
//this rule
if (isDynamicParent(queue)) {
return true;
}
}
//at this point we cannot find any parent which is eligible for creating
//this path
throw new YarnException("No eligible parent found on path '" + path + "'.");
}
/**
* This method determines if a queue is eligible for being a parent queue.
* Since YARN-10506 not only managed parent queues can have child queues.
* @param queue The queue object
* @return true if queues can be created under this queue otherwise false
*/
private boolean isDynamicParent(CSQueue queue) {
if (queue == null) {
return false;
}
if (queue instanceof ManagedParentQueue) {
return true;
}
if (queue instanceof ParentQueue) {
return ((ParentQueue)queue).isEligibleForAutoQueueCreation();
}
return false;
}
@ -186,6 +213,9 @@ private boolean validateDynamicQueuePath(MappingQueuePath path)
* @throws YarnException if the provided queue path is invalid
*/
public boolean validateQueuePath(String queuePath) throws YarnException {
if (queuePath == null || queuePath.isEmpty()) {
throw new YarnException("Queue path is empty.");
}
MappingQueuePath path = new MappingQueuePath(queuePath);
if (isPathStatic(queuePath)) {
@ -200,11 +230,17 @@ public boolean validateQueuePath(String queuePath) throws YarnException {
* A part is dynamic if a known variable is referenced in it.
* @param queuePath The path to check
* @return true if no dynamic parts were found
* @throws YarnException if a path part is invalid (eg. empty)
*/
public boolean isPathStatic(String queuePath) {
public boolean isPathStatic(String queuePath) throws YarnException {
String[] parts = queuePath.split("\\.");
for (int i = 0; i < parts.length; i++) {
if (knownVariables.contains(parts[i])) {
if (parts[i].isEmpty()) {
throw new YarnException("Path segment cannot be empty '" +
queuePath + "'.");
}
if (!isPathPartStatic(parts[i])) {
return false;
}
}
@ -212,6 +248,20 @@ public boolean isPathStatic(String queuePath) {
return true;
}
/**
* Method to determine if the provided queue path part is dynamic.
* A part is dynamic if a known variable is referenced in it.
* @param pathPart The path part to check
* @return true if part is not dynamic
*/
private boolean isPathPartStatic(String pathPart) {
if (knownVariables.contains(pathPart)) {
return false;
}
return true;
}
/**
* This method will add a known variable to the validation context, known
* variables can be used to determine if a path is static or dynamic.

View File

@ -0,0 +1,153 @@
/**
* 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.placement;
import org.apache.hadoop.yarn.exceptions.YarnException;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CSQueue;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacitySchedulerQueueManager;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.ManagedParentQueue;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.ParentQueue;
import java.util.ArrayList;
import java.util.Collections;
import static org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacitySchedulerConfiguration.DOT;
/**
* This class' functionality needs to be merged into CapacityScheduler
* or CapacitySchedulerQueueManager, but that will include a lot of testcase
* changes, so temporarily the logic is extracted to this class.
*/
public final class MappingRuleValidationHelper {
public enum ValidationResult {
CREATABLE,
QUEUE_EXISTS,
NO_PARENT_PROVIDED,
NO_DYNAMIC_PARENT,
AMBIGUOUS_PARENT,
AMBIGUOUS_QUEUE,
EMPTY_PATH
}
/**
* Utility class hidden constructor.
*/
private MappingRuleValidationHelper() {
}
public static String normalizeQueuePathRoot(
CapacitySchedulerQueueManager queueManager, String fullPath)
throws YarnException {
//Normalizing the root of the path
ArrayList<String> parts = new ArrayList<>();
Collections.addAll(parts, fullPath.split("\\."));
//the first element of the path is the path root
String pathRoot = parts.get(0);
CSQueue pathRootQueue = queueManager.getQueue(pathRoot);
if (pathRootQueue == null) {
if (queueManager.isAmbiguous(pathRoot)) {
throw new YarnException("Path root '" + pathRoot +
"' is ambiguous. Path '" + fullPath + "' is invalid");
} else {
throw new YarnException("Path root '" + pathRoot +
"' does not exist. Path '" + fullPath + "' is invalid");
}
}
//Normalizing the root
parts.set(0, pathRootQueue.getQueuePath());
return String.join(DOT, parts);
}
public static ValidationResult validateQueuePathAutoCreation(
CapacitySchedulerQueueManager queueManager, String path) {
//Some sanity checks, the empty path and existing queue can be checked easy
if (path == null || path.isEmpty()) {
return ValidationResult.EMPTY_PATH;
}
if (queueManager.getQueue(path) != null) {
return ValidationResult.QUEUE_EXISTS;
}
if (queueManager.isAmbiguous(path)) {
return ValidationResult.AMBIGUOUS_QUEUE;
}
//Creating the path of the parent queue and grand parent queue
ArrayList<String> parts = new ArrayList<>();
Collections.addAll(parts, path.split("\\."));
//dropping leaf name part of the path
parts.remove(parts.size() - 1);
String parentPath = parts.size() >= 1 ? String.join(".", parts) : "";
//dropping parent name part of the path
parts.remove(parts.size() - 1);
String grandParentPath = parts.size() >= 1 ? String.join(".", parts) : "";
if (parentPath.isEmpty()) {
return ValidationResult.NO_PARENT_PROVIDED;
}
if (queueManager.isAmbiguous(parentPath)) {
return ValidationResult.AMBIGUOUS_PARENT;
}
CSQueue parentQueue = queueManager.getQueue(parentPath);
if (parentQueue == null) {
if (grandParentPath.isEmpty()) {
return ValidationResult.NO_PARENT_PROVIDED;
}
if (queueManager.isAmbiguous(grandParentPath)) {
return ValidationResult.AMBIGUOUS_PARENT;
}
//if we don't have a valid parent queue, we need to check the grandparent
//if the grandparent allows new dynamic creation, the dynamic parent and
//the dynamic leaf queue can be created as well
CSQueue grandParentQueue = queueManager.getQueue(grandParentPath);
if (grandParentQueue != null && grandParentQueue instanceof ParentQueue &&
((ParentQueue)grandParentQueue).isEligibleForAutoQueueCreation()) {
//Grandparent is a new dynamic parent queue, which allows deep queue
//creation
return ValidationResult.CREATABLE;
}
return ValidationResult.NO_DYNAMIC_PARENT;
}
//at this point we know we have a parent queue we just need to make sure
//it allows queue creation
if (parentQueue instanceof ManagedParentQueue) {
//Managed parent is the legacy way, so it will allow creation
return ValidationResult.CREATABLE;
}
if (parentQueue instanceof ParentQueue) {
//the new way of dynamic queue creation uses ParentQueues so we need to
//check if those queues allow dynamic queue creation
if (((ParentQueue)parentQueue).isEligibleForAutoQueueCreation()) {
return ValidationResult.CREATABLE;
}
}
//at this point we can be sure the parent does not support auto queue
//creation it's either being a leaf queue or a non-dynamic parent queue
return ValidationResult.NO_DYNAMIC_PARENT;
}
}

View File

@ -21,7 +21,12 @@
import org.apache.hadoop.thirdparty.com.google.common.collect.Maps;
import org.apache.hadoop.thirdparty.com.google.common.collect.Sets;
import org.apache.commons.compress.utils.Lists;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.*;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.AbstractCSQueue;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CSQueue;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacitySchedulerQueueManager;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.LeafQueue;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.ManagedParentQueue;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.ParentQueue;
import java.util.ArrayList;
import java.util.List;
@ -36,6 +41,7 @@ class MockQueueHierarchyBuilder {
private static final String QUEUE_SEP = ".";
private List<String> queuePaths = Lists.newArrayList();
private List<String> managedParentQueues = Lists.newArrayList();
private List<String> dynamicParentQueues = Lists.newArrayList();
private Set<String> ambiguous = Sets.newHashSet();
private Map<String, String> shortNameMapping = Maps.newHashMap();
private CapacitySchedulerQueueManager queueManager;
@ -62,6 +68,12 @@ public MockQueueHierarchyBuilder withManagedParentQueue(
return this;
}
public MockQueueHierarchyBuilder withDynamicParentQueue(
String dynamicQueue) {
this.dynamicParentQueues.add(dynamicQueue);
return this;
}
public void build() {
if (this.queueManager == null) {
throw new IllegalStateException(
@ -77,6 +89,15 @@ public void build() {
}
}
for (String dynamicParentQueue : dynamicParentQueues) {
if (!queuePaths.contains(dynamicParentQueue)) {
queuePaths.add(dynamicParentQueue);
} else {
throw new IllegalStateException("Cannot add a dynamic parent " +
"and a simple queue with the same path");
}
}
Map<String, AbstractCSQueue> queues = Maps.newHashMap();
for (String queuePath : queuePaths) {
addQueues(queues, queuePath);
@ -128,10 +149,12 @@ private AbstractCSQueue createQueue(ParentQueue parentQueue,
return createRootQueue(ROOT);
} else if (managedParentQueues.contains(currentQueuePath)) {
return addManagedParentQueueAsChildOf(parentQueue, queueName);
} else if (dynamicParentQueues.contains(currentQueuePath)) {
return addParentQueueAsChildOf(parentQueue, queueName, true);
} else if (isLeaf) {
return addLeafQueueAsChildOf(parentQueue, queueName);
} else {
return addParentQueueAsChildOf(parentQueue, queueName);
return addParentQueueAsChildOf(parentQueue, queueName, false);
}
}
@ -144,8 +167,9 @@ private AbstractCSQueue createRootQueue(String rootQueueName) {
}
private AbstractCSQueue addParentQueueAsChildOf(ParentQueue parent,
String queueName) {
String queueName, boolean isDynamic) {
ParentQueue queue = mock(ParentQueue.class);
when(queue.isEligibleForAutoQueueCreation()).thenReturn(isDynamic);
setQueueFields(parent, queue, queueName);
return queue;
}

View File

@ -29,7 +29,7 @@
public class TestMappingRuleValidationContextImpl {
@Test
public void testContextVariables() {
public void testContextVariables() throws YarnException {
//Setting up queue manager and emulated queue hierarchy
CapacitySchedulerQueueManager qm =
mock(CapacitySchedulerQueueManager.class);
@ -79,7 +79,7 @@ void assertValidPath(MappingRuleValidationContext ctx, String path) {
try {
ctx.validateQueuePath(path);
} catch (YarnException e) {
fail("Path '" + path + "' should be VALID");
fail("Path '" + path + "' should be VALID: " + e);
}
}
@ -93,7 +93,7 @@ void assertInvalidPath(MappingRuleValidationContext ctx, String path) {
}
@Test
public void testDynamicQueueValidation() {
public void testManagedQueueValidation() {
//Setting up queue manager and emulated queue hierarchy
CapacitySchedulerQueueManager qm =
mock(CapacitySchedulerQueueManager.class);
@ -123,12 +123,53 @@ public void testDynamicQueueValidation() {
assertValidPath(ctx, "managed.%dynamic");
assertInvalidPath(ctx, "root.invalid.%dynamic");
assertInvalidPath(ctx, "root.umanaged.%dynamic");
assertInvalidPath(ctx, "root.unmanaged.%dynamic");
assertValidPath(ctx, "root.unmanagedwithchild.%user");
assertValidPath(ctx, "unmanagedwithchild.%user");
}
@Test
public void testDynamicQueueValidation() {
//Setting up queue manager and emulated queue hierarchy
CapacitySchedulerQueueManager qm =
mock(CapacitySchedulerQueueManager.class);
MockQueueHierarchyBuilder.create()
.withQueueManager(qm)
.withQueue("root.unmanaged")
.withDynamicParentQueue("root.managed")
.withQueue("root.unmanagedwithchild.child")
.withQueue("root.leaf")
.build();
when(qm.getQueue(isNull())).thenReturn(null);
MappingRuleValidationContextImpl ctx =
new MappingRuleValidationContextImpl(qm);
try {
ctx.addVariable("%dynamic");
ctx.addVariable("%user");
} catch (YarnException e) {
fail("We don't expect the add variable to fail: " + e.getMessage());
}
assertValidPath(ctx, "%dynamic");
assertValidPath(ctx, "root.%dynamic");
assertValidPath(ctx, "%user.%dynamic");
assertValidPath(ctx, "root.managed.%dynamic");
assertValidPath(ctx, "managed.%dynamic");
assertValidPath(ctx, "managed.static");
assertValidPath(ctx, "managed.static.%dynamic");
assertValidPath(ctx, "managed.static.%dynamic.%dynamic");
assertInvalidPath(ctx, "root.invalid.%dynamic");
assertInvalidPath(ctx, "root.unmanaged.%dynamic");
assertValidPath(ctx, "root.unmanagedwithchild.%user");
assertValidPath(ctx, "unmanagedwithchild.%user");
}
@Test
public void testStaticQueueValidation() {
//Setting up queue manager and emulated queue hierarchy
@ -142,6 +183,9 @@ public void testStaticQueueValidation() {
.withQueue("root.deep.queue.path")
.withQueue("root.ambi.ambileaf")
.withQueue("root.deep.ambi.ambileaf")
.withQueue("root.deep.ambi.very.deeepleaf")
.withDynamicParentQueue("root.dynamic")
.withQueue("root.dynamic.static.static")
.build();
when(qm.getQueue(isNull())).thenReturn(null);
@ -160,13 +204,22 @@ public void testStaticQueueValidation() {
assertInvalidPath(ctx, "ambi.ambileaf");
assertValidPath(ctx, "root.ambi.ambileaf");
assertInvalidPath(ctx, "root.dynamic.static");
assertValidPath(ctx, "root.dynamic.static.static");
//Invalid because static is already created as a non-dynamic parent queue
assertInvalidPath(ctx, "root.dynamic.static.any");
//Valid because 'any' is not created yet
assertValidPath(ctx, "root.dynamic.any.thing");
//Too deep, dynamic is the last dynamic parent
assertInvalidPath(ctx, "root.dynamic.any.thing.deep");
assertValidPath(ctx, "root.managed.a");
assertInvalidPath(ctx, "root.deep");
assertInvalidPath(ctx, "deep");
assertInvalidPath(ctx, "deep.queue");
assertInvalidPath(ctx, "root.deep.queue");
assertInvalidPath(ctx, "deep.queue.path");
assertValidPath(ctx, "deep.queue.path");
assertInvalidPath(ctx, "ambi.very.deeepleaf");
assertValidPath(ctx, "queue.path");
assertInvalidPath(ctx, "queue.invalidPath");
assertValidPath(ctx, "path");

View File

@ -436,7 +436,8 @@ public void testQueueMappingValidationFailsWithInvalidParentQueueInMapping()
//expected exception
assertTrue(e.getMessage().contains(
"Target queue path 'a1.%user' has a non-managed parent queue"));
"Queue path 'a1.%user' is invalid because 'root.a.a1' " +
"is a leaf queue"));
}
//"a" is not auto create enabled and app_user does not exist as a leaf
@ -450,7 +451,7 @@ public void testQueueMappingValidationFailsWithInvalidParentQueueInMapping()
} catch (IOException e) {
//expected exception
assertTrue(e.getMessage().contains(
"contains an invalid parent queue 'INVALID_PARENT_QUEUE'"));
"Path root 'INVALID_PARENT_QUEUE' does not exist."));
}
} finally {
if (newMockRM != null) {
@ -474,13 +475,14 @@ public void testQueueMappingUpdatesFailsOnRemovalOfParentQueueInMapping()
newCS.updatePlacementRules();
try {
setupQueueMapping(newCS, CURRENT_USER_MAPPING, "",
setupQueueMapping(newCS, CURRENT_USER_MAPPING, "nonexistent",
CURRENT_USER_MAPPING);
newCS.updatePlacementRules();
fail("Expected invalid parent queue mapping failure");
} catch (IOException e) {
//expected exception
assertTrue(e.getMessage().contains("invalid parent queue"));
assertTrue(
e.getMessage().contains("Path root 'nonexistent' does not exist."));
}
} finally {
if (newMockRM != null) {