diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/DefaultPlacementRule.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/DefaultPlacementRule.java new file mode 100644 index 00000000000..d462ad8b1a3 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/DefaultPlacementRule.java @@ -0,0 +1,134 @@ +/** + * 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 com.google.common.annotations.VisibleForTesting; +import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext; +import org.apache.hadoop.yarn.conf.YarnConfiguration; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.ResourceScheduler; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FSQueue; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.QueueManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Element; + +import java.io.IOException; + +import static org.apache.hadoop.yarn.server.resourcemanager.placement.FairQueuePlacementUtils.assureRoot; +import static org.apache.hadoop.yarn.server.resourcemanager.placement.FairQueuePlacementUtils.isValidQueueName; + +/** + * Places apps in the specified default queue. If no default queue is + * specified the app is placed in root.default queue. + */ +public class DefaultPlacementRule extends PlacementRule { + private static final Logger LOG = + LoggerFactory.getLogger(DefaultPlacementRule.class); + + private QueueManager queueManager; + @VisibleForTesting + public String defaultQueueName; + + /** + * Create a new rule from the xml config. + * @param conf An xml element from the {@link FairScheduler#conf} + */ + public DefaultPlacementRule(Element conf) { + // Default create for this rule is true + createQueue = true; + // No config can be set when no policy is defined and we use defaults + if (conf != null) { + String create = conf.getAttribute("create"); + if (create != null && !create.isEmpty()) { + createQueue = Boolean.parseBoolean(create); + } + defaultQueueName = conf.getAttribute("queue"); + } + // The queue name does not have to be set and we really use "default" + if (defaultQueueName == null || defaultQueueName.isEmpty()) { + defaultQueueName = assureRoot(YarnConfiguration.DEFAULT_QUEUE_NAME); + } else { + defaultQueueName = assureRoot(defaultQueueName); + } + LOG.debug("Default rule instantiated with queue name: {}, " + + "and create flag: {}", defaultQueueName, createQueue); + } + + /** + * Create a new rule just setting the create flag. + * @param create String form of the boolean flag, ignored by the rule. + */ + public DefaultPlacementRule(boolean create) { + createQueue = create; + // No config so fall back to the real default. + defaultQueueName = assureRoot(YarnConfiguration.DEFAULT_QUEUE_NAME); + LOG.debug("Default rule instantiated with default queue name: {}, " + + "and create flag: {}", defaultQueueName, createQueue); + } + + @Override + public boolean initialize(ResourceScheduler scheduler) throws IOException { + if (!(scheduler instanceof FairScheduler)) { + LOG.error("Default rule configured for wrong scheduler type."); + throw new IOException( + "Default rule can only be configured for the FairScheduler"); + } + // A queue read from the config could be illegal check it + if (!isValidQueueName(defaultQueueName)) { + LOG.error("Default rule configured with an illegal queue name: '{}'", + defaultQueueName); + throw new IOException( + "Default rule configured with an illegal queue name: '" + + defaultQueueName + "'."); + } + if (getParentRule() != null) { + LOG.error("You cannot configure a parent rule for the Default rule."); + throw new IOException( + "Parent rule should not be configured for Default rule."); + } + + FairScheduler fs = (FairScheduler) scheduler; + queueManager = fs.getQueueManager(); + + return true; + } + + @Override + public ApplicationPlacementContext getPlacementForApp( + ApplicationSubmissionContext asc, String user) { + + // If we can create the queue in the rule or the queue exists return it + if (createQueue || configuredQueue(defaultQueueName)) { + return new ApplicationPlacementContext(defaultQueueName); + } + return null; + } + + /** + * Check if the queue exists and is part of the configuration i.e. not + * a {@link FSQueue#isDynamic()} queue. + * @param queueName name of the queue to check + * @return true if the queue exists and is a "configured" queue + */ + private boolean configuredQueue(String queueName) { + FSQueue queue = queueManager.getQueue(queueName); + return (queue != null && !queue.isDynamic()); + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/FairQueuePlacementUtils.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/FairQueuePlacementUtils.java new file mode 100644 index 00000000000..6e59d57589a --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/FairQueuePlacementUtils.java @@ -0,0 +1,96 @@ +/** + * 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.server.resourcemanager.scheduler.fair.FairSchedulerUtilities; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utility methods used by Fair scheduler placement rules. + * {@link + * org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler} + */ +public final class FairQueuePlacementUtils { + private static final Logger LOG = + LoggerFactory.getLogger(QueuePlacementRuleUtils.class); + + private static final String ROOT_QUEUE = "root"; + // Constants for name clean up and hierarchy checks + private static final String DOT_REPLACEMENT = "_dot_"; + protected static final String DOT = "."; + + private FairQueuePlacementUtils() { + } + + /** + * Replace the periods in the username or group name with "_dot_" and + * remove trailing and leading whitespace. + * + * @param name The name to clean + * @return The name with {@link #DOT} replaced with {@link #DOT_REPLACEMENT} + */ + protected static String cleanName(String name) { + name = FairSchedulerUtilities.trimQueueName(name); + if (name.contains(DOT)) { + String converted = name.replaceAll("\\.", DOT_REPLACEMENT); + LOG.warn("Name {} is converted to {} when it is used as a queue name.", + name, converted); + return converted; + } else { + return name; + } + } + + /** + * Assure root prefix for a queue name. + * + * @param queueName The queue name to check for the root prefix + * @return The root prefixed queue name + */ + protected static String assureRoot(String queueName) { + if (queueName != null && !queueName.isEmpty()) { + if (!queueName.startsWith(ROOT_QUEUE + DOT) && + !queueName.equals(ROOT_QUEUE)) { + queueName = ROOT_QUEUE + DOT + queueName; + } + } else { + LOG.warn("AssureRoot: queueName is empty or null."); + } + return queueName; + } + + /** + * Validate the queue name: it may not start or end with a {@link #DOT}. + * + * @param queueName The queue name to validate + * @return false if the queue name starts or ends with a + * {@link #DOT}, true + */ + protected static boolean isValidQueueName(String queueName) { + if (queueName != null) { + if (queueName.equals(FairSchedulerUtilities.trimQueueName(queueName)) && + !queueName.startsWith(DOT) && + !queueName.endsWith(DOT)) { + return true; + } + } + return false; + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/PlacementFactory.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/PlacementFactory.java index a5de66463bb..76177120365 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/PlacementFactory.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/PlacementFactory.java @@ -23,6 +23,8 @@ import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.util.ReflectionUtils; +import java.lang.reflect.Constructor; + /** * Factory class for creating instances of {@link PlacementRule}. */ @@ -42,4 +44,27 @@ public static PlacementRule getPlacementRule(String ruleStr, LOG.info("Using PlacementRule implementation - " + ruleClass); return ReflectionUtils.newInstance(ruleClass, conf); } -} \ No newline at end of file + + /** + * Create a new {@link PlacementRule} based on the rule class from the + * configuration. This is used to instantiate rules with different + * constructors by the FairScheduler. + * @param A class that represent a {@link PlacementRule} + * @param theClass The specific class reference to instantiate + * @param classType argument type of the constructor + * @param initArg argument for the constructor + * @return Created class instance + */ + public static T getPlacementRule(Class theClass, Class classType, + Object initArg) { + T result; + try { + Constructor method = theClass.getDeclaredConstructor(classType); + method.setAccessible(true); + result = method.newInstance(initArg); + } catch (Exception e) { + throw new RuntimeException(e); + } + return result; + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/PlacementRule.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/PlacementRule.java index 0f3d43c5ad7..e5583951ffc 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/PlacementRule.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/PlacementRule.java @@ -20,11 +20,35 @@ import java.io.IOException; +import com.google.common.annotations.VisibleForTesting; import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext; import org.apache.hadoop.yarn.exceptions.YarnException; import org.apache.hadoop.yarn.server.resourcemanager.scheduler.ResourceScheduler; +/** + * Abstract base for all Placement Rules. + */ public abstract class PlacementRule { + // Flag to show if the rule can create a queue + @VisibleForTesting + public boolean createQueue = false; + private PlacementRule parentRule = null; + + /** + * Set a rule to generate the parent queue dynamically. + * @param parent A PlacementRule + */ + public void setParentRule(PlacementRule parent) { + this.parentRule = parent; + } + + /** + * Get the rule that is set to generate the parent queue dynamically. + * @return The rule set or null if not set. + */ + public PlacementRule getParentRule() { + return parentRule; + } public String getName() { return this.getClass().getName(); @@ -34,7 +58,7 @@ public abstract boolean initialize( ResourceScheduler scheduler) throws IOException; /** - * Get queue for a given application + * Get queue for a given application. * * @param asc application submission context * @param user userName diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/PrimaryGroupPlacementRule.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/PrimaryGroupPlacementRule.java new file mode 100644 index 00000000000..b97de30317d --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/PrimaryGroupPlacementRule.java @@ -0,0 +1,158 @@ +/** + * 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.security.Groups; +import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext; +import org.apache.hadoop.yarn.exceptions.YarnException; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.ResourceScheduler; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FSLeafQueue; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FSQueue; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.QueueManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Element; + +import java.io.IOException; +import java.util.List; + +import static org.apache.hadoop.yarn.server.resourcemanager.placement.FairQueuePlacementUtils.DOT; +import static org.apache.hadoop.yarn.server.resourcemanager.placement.FairQueuePlacementUtils.assureRoot; +import static org.apache.hadoop.yarn.server.resourcemanager.placement.FairQueuePlacementUtils.cleanName; + +/** + * Places apps in queues by the primary group of the submitter. + */ +public class PrimaryGroupPlacementRule extends PlacementRule { + private static final Logger LOG = + LoggerFactory.getLogger(PrimaryGroupPlacementRule.class); + + private QueueManager queueManager; + private Groups groupProvider; + + /** + * Create a new rule from the xml config. + * @param conf An xml element from the {@link FairScheduler#conf} + */ + public PrimaryGroupPlacementRule(Element conf) { + // No config can be set when no policy is defined and we use defaults + createQueue = true; + if (conf != null) { + String create = conf.getAttribute("create"); + if (create != null && !create.isEmpty()) { + createQueue = Boolean.parseBoolean(create); + } + } + LOG.debug("PrimaryGroupPlacementRule instantiated with create flag: {}", + createQueue); + } + + /** + * Create a new rule just setting the create flag. + * @param create String form of the boolean flag. Only true + * will set the flag to True + */ + public PrimaryGroupPlacementRule(boolean create) { + createQueue = create; + LOG.debug("PrimaryGroupPlacementRule instantiated with create flag: {}", + createQueue); + } + + @Override + public boolean initialize(ResourceScheduler scheduler) throws IOException { + if (!(scheduler instanceof FairScheduler)) { + LOG.error("PrimaryGroup rule configured for wrong scheduler type."); + throw new IOException( + "PrimaryGroup rule can only be configured for the FairScheduler"); + } + if (getParentRule() != null && + getParentRule().getName().equals(getName())) { + LOG.error("PrimaryGroup rule has parent rule of the same type " + + "configured. You may not configure parent and child rule of the " + + "same type."); + throw new IOException( + "Parent rule should not be the same type as the child rule " + + "(PrimaryGroup)"); + } + + FairScheduler fs = (FairScheduler) scheduler; + queueManager = fs.getQueueManager(); + groupProvider = new Groups(fs.getConfig()); + + return true; + } + + @Override + public ApplicationPlacementContext getPlacementForApp( + ApplicationSubmissionContext asc, String user) throws YarnException { + + // All users should have at least one group the primary group. If no groups + // are returned then there is a real issue. + final List groupList; + try { + groupList = groupProvider.getGroups(user); + } catch (IOException ioe) { + throw new YarnException("Group resolution failed", ioe); + } + if (groupList.isEmpty()) { + LOG.error("Group placement rule failed: No groups returned for user {}", + user); + throw new YarnException("No groups returned for user " + user); + } + + String cleanGroup = cleanName(groupList.get(0)); + String queueName; + PlacementRule parentRule = getParentRule(); + + if (getParentRule() != null) { + LOG.debug("PrimaryGroup rule: parent rule found: {}", + parentRule.getName()); + ApplicationPlacementContext parent = + parentRule.getPlacementForApp(asc, user); + if (parent == null || + queueManager.getQueue(parent.getQueue()) instanceof FSLeafQueue) { + LOG.debug("PrimaryGroup rule: parent rule failed"); + return null; + } + LOG.debug("PrimaryGroup rule: parent rule result: {}", + parent.getQueue()); + queueName = parent.getQueue() + DOT + cleanGroup; + } else { + queueName = assureRoot(cleanGroup); + } + + // If we can create the queue in the rule or the queue exists return it + if (createQueue || configuredQueue(queueName)) { + return new ApplicationPlacementContext(queueName); + } + return null; + } + + /** + * Check if the queue exists and is part of the configuration i.e. not + * a {@link FSQueue#isDynamic()} queue. + * @param queueName name of the queue to check + * @return true if the queue exists and is a "configured" queue + */ + private boolean configuredQueue(String queueName) { + FSQueue queue = queueManager.getQueue(queueName); + return (queue != null && !queue.isDynamic()); + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/RejectPlacementRule.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/RejectPlacementRule.java new file mode 100644 index 00000000000..e5a9dc5b12f --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/RejectPlacementRule.java @@ -0,0 +1,76 @@ +/** + * 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.api.records.ApplicationSubmissionContext; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.ResourceScheduler; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Element; + +import java.io.IOException; + +/** + * Places apps in queues by username of the submitter. + */ +public class RejectPlacementRule extends PlacementRule { + private static final Logger LOG = + LoggerFactory.getLogger(RejectPlacementRule.class); + + /** + * Create a new rule from the xml config. All placements are + * rejected and the parameters are ignored. + * @param conf An xml element from the {@link FairScheduler#conf} + */ + public RejectPlacementRule(Element conf) { + LOG.debug("RejectPlacementRule instantiated"); + } + + /** + * Create a new rule just setting the create flag. All placements are + * rejected and the parameters are ignored. + * @param create Create flag (ignored in this rule) + */ + public RejectPlacementRule(boolean create) { + LOG.debug("RejectPlacementRule instantiated"); + } + + @Override + public boolean initialize(ResourceScheduler scheduler) throws IOException { + if (!(scheduler instanceof FairScheduler)) { + LOG.error("Reject rule configured for wrong scheduler type."); + throw new IOException( + "Reject rule can only be configured for the FairScheduler"); + } + if (getParentRule() != null) { + LOG.error("You cannot configure a parent rule for the Reject rule."); + throw new IOException( + "Parent rule should not be configured for Reject rule."); + } + + return true; + } + + @Override + public ApplicationPlacementContext getPlacementForApp( + ApplicationSubmissionContext asc, String user) { + return null; + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/SecondaryGroupExistingPlacementRule.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/SecondaryGroupExistingPlacementRule.java new file mode 100644 index 00000000000..ed73f689bea --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/SecondaryGroupExistingPlacementRule.java @@ -0,0 +1,157 @@ +/** + * 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.security.Groups; +import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext; +import org.apache.hadoop.yarn.exceptions.YarnException; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.ResourceScheduler; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FSLeafQueue; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FSQueue; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.QueueManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Element; + +import java.io.IOException; +import java.util.List; + +import static org.apache.hadoop.yarn.server.resourcemanager.placement.FairQueuePlacementUtils.DOT; +import static org.apache.hadoop.yarn.server.resourcemanager.placement.FairQueuePlacementUtils.assureRoot; +import static org.apache.hadoop.yarn.server.resourcemanager.placement.FairQueuePlacementUtils.cleanName; + +/** + * Places apps in queues by the secondary group of the submitter, if the + * submitter is a member of more than one group. + * The first "matching" queue based on the group list is returned. The match + * takes into account the parent rule and create flag, + */ +public class SecondaryGroupExistingPlacementRule extends PlacementRule { + private static final Logger LOG = + LoggerFactory.getLogger(SecondaryGroupExistingPlacementRule.class); + + private QueueManager queueManager; + private Groups groupProvider; + + /** + * Create a new rule from the xml config. + * @param conf An xml element from the {@link FairScheduler#conf} + */ + public SecondaryGroupExistingPlacementRule(Element conf) { + // No config can be set when no policy is defined and we use defaults + createQueue = true; + if (conf != null) { + String create = conf.getAttribute("create"); + if (create != null && !create.isEmpty()) { + createQueue = Boolean.parseBoolean(create); + } + } + LOG.debug("SecondaryGroupExistingPlacementRule instantiated with create " + + "flag: {}", createQueue); + } + + /** + * Create a new rule just setting the create flag. + * @param create String form of the boolean flag. Only true + * will set the flag to True + */ + public SecondaryGroupExistingPlacementRule(boolean create) { + createQueue = create; + LOG.debug("SecondaryGroupExistingPlacementRule instantiated with create " + + "flag: {}", createQueue); + } + + @Override + public boolean initialize(ResourceScheduler scheduler) throws IOException { + if (!(scheduler instanceof FairScheduler)) { + LOG.error( + "SecondaryGroupExisting rule configured for wrong scheduler type."); + throw new IOException("SecondaryGroupExisting rule can only be " + + "configured for the FairScheduler"); + } + if (getParentRule() != null && + getParentRule().getName().equals(getName())) { + LOG.error("SecondaryGroupExisting rule has parent rule of the same " + + "type configured. You may not configure parent and child rule of " + + "the same type."); + throw new IOException("Parent rule should not be the same type as the " + + "child rule (SecondaryGroupExisting)"); + } + + FairScheduler fs = (FairScheduler) scheduler; + queueManager = fs.getQueueManager(); + groupProvider = new Groups(fs.getConfig()); + + return true; + } + + @Override + public ApplicationPlacementContext getPlacementForApp( + ApplicationSubmissionContext asc, String user) throws YarnException { + + // All users should have at least one group the primary group. If no groups + // are returned then there is a real issue. + final List groupList; + try { + groupList = groupProvider.getGroups(user); + } catch (IOException ioe) { + throw new YarnException("Group resolution failed", ioe); + } + + String parentQueue = null; + PlacementRule parentRule = getParentRule(); + + if (parentRule != null) { + LOG.debug("SecondaryGroupExisting rule: parent rule found: {}", + parentRule.getName()); + ApplicationPlacementContext parent = + parentRule.getPlacementForApp(asc, user); + if (parent == null || + queueManager.getQueue(parent.getQueue()) instanceof FSLeafQueue) { + LOG.debug("SecondaryGroupExisting rule: parent rule failed"); + return null; + } + parentQueue = parent.getQueue(); + LOG.debug("SecondaryGroupExisting rule: parent rule result: {}", + parentQueue); + } + // now check the groups inside the parent + for (int i = 1; i < groupList.size(); i++) { + String group = cleanName(groupList.get(i)); + String queueName = + parentQueue == null ? assureRoot(group) : parentQueue + DOT + group; + if (configuredQueue(queueName)) { + return new ApplicationPlacementContext(queueName); + } + } + return null; + } + + /** + * Check if the queue exists and is part of the configuration i.e. not + * a {@link FSQueue#isDynamic()} queue. + * @param queueName name of the queue to check + * @return true if the queue exists and is a "configured" queue + */ + private boolean configuredQueue(String queueName) { + FSQueue queue = queueManager.getQueue(queueName); + return (queue != null && !queue.isDynamic()); + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/SpecifiedPlacementRule.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/SpecifiedPlacementRule.java new file mode 100644 index 00000000000..b86a25e248e --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/SpecifiedPlacementRule.java @@ -0,0 +1,128 @@ +/** + * 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.api.records.ApplicationSubmissionContext; +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.fair.FSQueue; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.QueueManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Element; + +import java.io.IOException; + +import static org.apache.hadoop.yarn.server.resourcemanager.placement.FairQueuePlacementUtils.assureRoot; +import static org.apache.hadoop.yarn.server.resourcemanager.placement.FairQueuePlacementUtils.isValidQueueName; + +/** + * Places apps in queues by requested queue of the submitter. + */ +public class SpecifiedPlacementRule extends PlacementRule { + private static final Logger LOG = + LoggerFactory.getLogger(SpecifiedPlacementRule.class); + + private QueueManager queueManager; + + /** + * Create a new rule from the xml config. + * @param conf An xml element from the {@link FairScheduler#conf} + */ + public SpecifiedPlacementRule(Element conf) { + createQueue = true; + // No config can be set when no policy is defined and we use defaults + if (conf != null) { + String create = conf.getAttribute("create"); + if (create != null && !create.isEmpty()) { + createQueue = Boolean.parseBoolean(create); + } + } + LOG.debug("Specified rule instantiated with create flag: {}", + createQueue); + } + + /** + * Create a new rule just setting the create flag. + * @param create String form of the boolean flag. Only true + * will set the flag to True + */ + public SpecifiedPlacementRule(boolean create) { + createQueue = create; + LOG.debug("Specified rule instantiated with create flag: {}", + createQueue); + } + + @Override + public boolean initialize(ResourceScheduler scheduler) throws IOException { + if (!(scheduler instanceof FairScheduler)) { + LOG.error("Specified rule configured for wrong scheduler type."); + throw new IOException( + "Specified rule can only be configured for the FairScheduler"); + } + if (getParentRule() != null) { + LOG.error( + "You cannot configure any parent rule for the Specified rule."); + throw new IOException( + "Parent rule should not be configured for Specified rule."); + } + + FairScheduler fs = (FairScheduler) scheduler; + queueManager = fs.getQueueManager(); + + return true; + } + + @Override + public ApplicationPlacementContext getPlacementForApp( + ApplicationSubmissionContext asc, String user) throws YarnException { + + // Sanity check the provided queue + String queueName = asc.getQueue(); + if (!isValidQueueName(queueName)) { + LOG.error("Specified queue name not valid: '{}'", queueName); + throw new YarnException("Application submitted by user " + user + + "with illegal queue name '" + queueName + "'."); + } + // On submission the requested queue will be set to "default" if no queue + // is specified: just check the next rule in that case + if (queueName.equals(YarnConfiguration.DEFAULT_QUEUE_NAME)) { + return null; + } + queueName = assureRoot(queueName); + // If we can create the queue in the rule or the queue exists return it + if (createQueue || configuredQueue(queueName)) { + return new ApplicationPlacementContext(queueName); + } + return null; + } + + /** + * Check if the queue exists and is part of the configuration i.e. not + * a {@link FSQueue#isDynamic()} queue. + * @param queueName name of the queue to check + * @return true if the queue exists and is a "configured" queue + */ + private boolean configuredQueue(String queueName) { + FSQueue queue = queueManager.getQueue(queueName); + return (queue != null && !queue.isDynamic()); + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/UserPlacementRule.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/UserPlacementRule.java new file mode 100644 index 00000000000..a18650c2450 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/UserPlacementRule.java @@ -0,0 +1,135 @@ +/** + * 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.api.records.ApplicationSubmissionContext; +import org.apache.hadoop.yarn.exceptions.YarnException; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.ResourceScheduler; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FSLeafQueue; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FSQueue; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler; +import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.QueueManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Element; + +import java.io.IOException; + +import static org.apache.hadoop.yarn.server.resourcemanager.placement.FairQueuePlacementUtils.DOT; +import static org.apache.hadoop.yarn.server.resourcemanager.placement.FairQueuePlacementUtils.assureRoot; +import static org.apache.hadoop.yarn.server.resourcemanager.placement.FairQueuePlacementUtils.cleanName; + +/** + * Places apps in queues by username of the submitter. + */ +public class UserPlacementRule extends PlacementRule { + private static final Logger LOG = + LoggerFactory.getLogger(UserPlacementRule.class); + + private QueueManager queueManager; + + /** + * Create a new rule from the xml config. + * @param conf An xml element from the {@link FairScheduler#conf} + */ + public UserPlacementRule(Element conf) { + // No config can be set when no policy is defined and we use defaults + createQueue = true; + if (conf != null) { + String create = conf.getAttribute("create"); + if (create != null && !create.isEmpty()) { + createQueue = Boolean.parseBoolean(create); + } + } + LOG.debug("User rule instantiated with create flag: {}", + createQueue); + } + + /** + * Create a new rule just setting the create flag. + * @param create String form of the boolean flag. Only true + * will set the flag to True + */ + public UserPlacementRule(boolean create) { + createQueue = create; + LOG.debug("User rule instantiated with create flag: {}", + createQueue); + } + + @Override + public boolean initialize(ResourceScheduler scheduler) throws IOException { + if (!(scheduler instanceof FairScheduler)) { + LOG.error("User rule configured for wrong scheduler type."); + throw new IOException( + "User rule can only be configured for the FairScheduler"); + } + if (getParentRule() != null && + getParentRule().getName().equals(getName())) { + LOG.error("User rule has parent rule of the same type configured. You " + + "may not configure parent and child rule of the same type."); + throw new IOException( + "Parent rule should not be the same type as the child rule (User)"); + } + + FairScheduler fs = (FairScheduler) scheduler; + queueManager = fs.getQueueManager(); + + return true; + } + + @Override + public ApplicationPlacementContext getPlacementForApp( + ApplicationSubmissionContext asc, String user) throws YarnException { + String queueName; + + String cleanUser = cleanName(user); + PlacementRule parentRule = getParentRule(); + if (parentRule != null) { + LOG.debug("User rule: parent rule found: {}", parentRule.getName()); + ApplicationPlacementContext parent = + parentRule.getPlacementForApp(asc, user); + if (parent == null || + queueManager.getQueue(parent.getQueue()) instanceof FSLeafQueue) { + LOG.debug("User rule: parent rule failed"); + return null; + } + LOG.debug("User rule: parent rule result: {}", parent.getQueue()); + queueName = parent.getQueue() + DOT + cleanUser; + } else { + queueName = assureRoot(cleanUser); + } + + // If we can create the queue in the rule or the queue exists return it + if (createQueue || configuredQueue(queueName)) { + return new ApplicationPlacementContext(queueName); + } + return null; + } + + /** + * Check if the queue exists and is part of the configuration i.e. not + * a {@link FSQueue#isDynamic()} queue. + * @param queueName name of the queue to check + * @return true if the queue exists and is a "configured" queue + */ + private boolean configuredQueue(String queueName) { + FSQueue queue = queueManager.getQueue(queueName); + return (queue != null && !queue.isDynamic()); + } +}