|
@@ -0,0 +1,230 @@
|
|
|
|
+/**
|
|
|
|
+ * 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.scheduler.capacity;
|
|
|
|
+
|
|
|
|
+import java.io.IOException;
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
+import java.util.Collection;
|
|
|
|
+import java.util.HashMap;
|
|
|
|
+import java.util.List;
|
|
|
|
+import java.util.Map;
|
|
|
|
+
|
|
|
|
+import org.apache.hadoop.classification.InterfaceAudience.Private;
|
|
|
|
+import org.apache.hadoop.security.UserGroupInformation;
|
|
|
|
+import org.apache.hadoop.util.StringUtils;
|
|
|
|
+import org.apache.hadoop.yarn.api.records.ApplicationId;
|
|
|
|
+import org.apache.hadoop.yarn.api.records.Priority;
|
|
|
|
+import org.apache.hadoop.yarn.conf.YarnConfiguration;
|
|
|
|
+import org.apache.hadoop.yarn.exceptions.YarnException;
|
|
|
|
+import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMApp;
|
|
|
|
+import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMAppImpl;
|
|
|
|
+import org.slf4j.Logger;
|
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
|
+
|
|
|
|
+import com.google.common.annotations.VisibleForTesting;
|
|
|
|
+
|
|
|
|
+@Private
|
|
|
|
+@VisibleForTesting
|
|
|
|
+public class WorkflowPriorityMappingsManager {
|
|
|
|
+
|
|
|
|
+ private static final Logger LOG =
|
|
|
|
+ LoggerFactory.getLogger(WorkflowPriorityMappingsManager.class);
|
|
|
|
+
|
|
|
|
+ private static final String WORKFLOW_PART_SEPARATOR = ":";
|
|
|
|
+
|
|
|
|
+ private static final String WORKFLOW_SEPARATOR = ",";
|
|
|
|
+
|
|
|
|
+ private CapacityScheduler scheduler;
|
|
|
|
+
|
|
|
|
+ private CapacitySchedulerConfiguration conf;
|
|
|
|
+
|
|
|
|
+ private boolean overrideWithPriorityMappings = false;
|
|
|
|
+ // Map of queue to a map of workflow ID to priority
|
|
|
|
+ private Map<String, Map<String, WorkflowPriorityMapping>> priorityMappings =
|
|
|
|
+ new HashMap<String, Map<String, WorkflowPriorityMapping>>();
|
|
|
|
+
|
|
|
|
+ public static class WorkflowPriorityMapping {
|
|
|
|
+ String workflowID;
|
|
|
|
+ String queue;
|
|
|
|
+ Priority priority;
|
|
|
|
+
|
|
|
|
+ public WorkflowPriorityMapping(String workflowID, String queue,
|
|
|
|
+ Priority priority) {
|
|
|
|
+ this.workflowID = workflowID;
|
|
|
|
+ this.queue = queue;
|
|
|
|
+ this.priority = priority;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public Priority getPriority() {
|
|
|
|
+ return this.priority;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public int hashCode() {
|
|
|
|
+ return super.hashCode();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean equals(Object obj) {
|
|
|
|
+ if (obj instanceof WorkflowPriorityMapping) {
|
|
|
|
+ WorkflowPriorityMapping other = (WorkflowPriorityMapping) obj;
|
|
|
|
+ return (other.workflowID.equals(workflowID) &&
|
|
|
|
+ other.queue.equals(queue) &&
|
|
|
|
+ other.priority.equals(priority));
|
|
|
|
+ } else {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public String toString() {
|
|
|
|
+ return workflowID + WORKFLOW_PART_SEPARATOR + queue
|
|
|
|
+ + WORKFLOW_PART_SEPARATOR + priority.getPriority();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @VisibleForTesting
|
|
|
|
+ public void initialize(CapacityScheduler scheduler) throws IOException {
|
|
|
|
+ this.scheduler = scheduler;
|
|
|
|
+ this.conf = scheduler.getConfiguration();
|
|
|
|
+ boolean overrideWithWorkflowPriorityMappings =
|
|
|
|
+ conf.getOverrideWithWorkflowPriorityMappings();
|
|
|
|
+ LOG.info("Initialized workflow priority mappings, override: "
|
|
|
|
+ + overrideWithWorkflowPriorityMappings);
|
|
|
|
+ this.overrideWithPriorityMappings = overrideWithWorkflowPriorityMappings;
|
|
|
|
+ this.priorityMappings = getWorkflowPriorityMappings();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Get workflow ID to priority mappings for a queue.
|
|
|
|
+ *
|
|
|
|
+ * @return workflowID to priority mappings for a queue
|
|
|
|
+ */
|
|
|
|
+ public Map<String, Map<String, WorkflowPriorityMapping>>
|
|
|
|
+ getWorkflowPriorityMappings() {
|
|
|
|
+ Map<String, Map<String, WorkflowPriorityMapping>> mappings =
|
|
|
|
+ new HashMap<String, Map<String, WorkflowPriorityMapping>>();
|
|
|
|
+
|
|
|
|
+ Collection<String> workflowMappings = conf.getWorkflowPriorityMappings();
|
|
|
|
+ for (String workflowMapping : workflowMappings) {
|
|
|
|
+ WorkflowPriorityMapping mapping =
|
|
|
|
+ getWorkflowMappingFromString(workflowMapping);
|
|
|
|
+ if (mapping != null) {
|
|
|
|
+ if (!mappings.containsKey(mapping.queue)) {
|
|
|
|
+ mappings.put(mapping.queue,
|
|
|
|
+ new HashMap<String, WorkflowPriorityMapping>());
|
|
|
|
+ }
|
|
|
|
+ mappings.get(mapping.queue).put(mapping.workflowID, mapping);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return mappings;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private WorkflowPriorityMapping getWorkflowMappingFromString(
|
|
|
|
+ String mappingString) {
|
|
|
|
+ if (mappingString == null) {
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+ String[] mappingArray = StringUtils
|
|
|
|
+ .getTrimmedStringCollection(mappingString, WORKFLOW_PART_SEPARATOR)
|
|
|
|
+ .toArray(new String[] {});
|
|
|
|
+ if (mappingArray.length != 3 || mappingArray[0].length() == 0
|
|
|
|
+ || mappingArray[1].length() == 0 || mappingArray[2].length() == 0) {
|
|
|
|
+ throw new IllegalArgumentException(
|
|
|
|
+ "Illegal workflow priority mapping " + mappingString);
|
|
|
|
+ }
|
|
|
|
+ WorkflowPriorityMapping mapping;
|
|
|
|
+ try {
|
|
|
|
+ mapping = new WorkflowPriorityMapping(mappingArray[0], mappingArray[1],
|
|
|
|
+ Priority.newInstance(Integer.parseInt(mappingArray[2])));
|
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
|
+ throw new IllegalArgumentException(
|
|
|
|
+ "Illegal workflow priority for mapping " + mappingString);
|
|
|
|
+ }
|
|
|
|
+ return mapping;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public Priority getMappedPriority(String workflowID, CSQueue queue) {
|
|
|
|
+ // Recursively fetch the priority mapping for the given workflow tracing
|
|
|
|
+ // up the queue hierarchy until the first match.
|
|
|
|
+ if (queue.equals(scheduler.getRootQueue())) {
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+ String queuePath = queue.getQueuePath();
|
|
|
|
+ if (priorityMappings.containsKey(queuePath)
|
|
|
|
+ && priorityMappings.get(queuePath).containsKey(workflowID)) {
|
|
|
|
+ return priorityMappings.get(queuePath).get(workflowID).priority;
|
|
|
|
+ } else {
|
|
|
|
+ queue = queue.getParent();
|
|
|
|
+ return getMappedPriority(workflowID, queue);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public Priority mapWorkflowPriorityForApp(ApplicationId applicationId,
|
|
|
|
+ CSQueue queue, String user, Priority priority) throws YarnException {
|
|
|
|
+ if (overrideWithPriorityMappings) {
|
|
|
|
+ // Set the correct workflow priority
|
|
|
|
+ RMApp rmApp = scheduler.getRMContext().getRMApps().get(applicationId);
|
|
|
|
+ if (rmApp != null && rmApp.getApplicationTags() != null
|
|
|
|
+ && rmApp.getApplicationSubmissionContext() != null) {
|
|
|
|
+ String workflowTagPrefix = scheduler.getConf().get(
|
|
|
|
+ YarnConfiguration.YARN_WORKFLOW_ID_TAG_PREFIX,
|
|
|
|
+ YarnConfiguration.DEFAULT_YARN_WORKFLOW_ID_TAG_PREFIX);
|
|
|
|
+ String workflowID = null;
|
|
|
|
+ for(String tag : rmApp.getApplicationTags()) {
|
|
|
|
+ if (tag.trim().startsWith(workflowTagPrefix)) {
|
|
|
|
+ workflowID = tag.trim().substring(workflowTagPrefix.length());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (workflowID != null && !workflowID.isEmpty()
|
|
|
|
+ && priorityMappings != null && priorityMappings.size() > 0) {
|
|
|
|
+ Priority mappedPriority = getMappedPriority(workflowID, queue);
|
|
|
|
+ if (mappedPriority != null) {
|
|
|
|
+ LOG.info("Application " + applicationId + " user " + user
|
|
|
|
+ + " workflow " + workflowID + " queue " + queue.getQueueName()
|
|
|
|
+ + " mapping [" + priority + "] to [" + mappedPriority
|
|
|
|
+ + "] override " + overrideWithPriorityMappings);
|
|
|
|
+
|
|
|
|
+ // If workflow ID exists in workflow mapping, change this
|
|
|
|
+ // application's priority to mapped value. Else, use queue
|
|
|
|
+ // default priority.
|
|
|
|
+ priority = mappedPriority;
|
|
|
|
+ priority = scheduler.checkAndGetApplicationPriority(
|
|
|
|
+ priority, UserGroupInformation.createRemoteUser(user),
|
|
|
|
+ queue.getQueueName(), applicationId);
|
|
|
|
+ rmApp.getApplicationSubmissionContext().setPriority(priority);
|
|
|
|
+ ((RMAppImpl)rmApp).setApplicationPriority(priority);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return priority;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public static String getWorkflowPriorityMappingStr(
|
|
|
|
+ List<WorkflowPriorityMapping> workflowPriorityMappings) {
|
|
|
|
+ if (workflowPriorityMappings == null) {
|
|
|
|
+ return "";
|
|
|
|
+ }
|
|
|
|
+ List<String> workflowPriorityMappingStrs = new ArrayList<>();
|
|
|
|
+ for (WorkflowPriorityMapping mapping : workflowPriorityMappings) {
|
|
|
|
+ workflowPriorityMappingStrs.add(mapping.toString());
|
|
|
|
+ }
|
|
|
|
+ return StringUtils.join(WORKFLOW_SEPARATOR, workflowPriorityMappingStrs);
|
|
|
|
+ }
|
|
|
|
+}
|