From 7e357a5e6fe99213607300fab9a69968a3d71ed0 Mon Sep 17 00:00:00 2001 From: Sunil Date: Tue, 8 Nov 2016 22:38:33 +0530 Subject: [PATCH] YARN-3955 --- .../hadoop/yarn/api/records/PriorityACL.java | 40 ++++ .../apache/hadoop/yarn/security/AccessType.java | 1 + .../yarn/security/ConfiguredYarnAuthorizer.java | 4 +- .../hadoop/yarn/security/PrivilegedEntity.java | 3 +- .../server/resourcemanager/ClientRMService.java | 13 ++ .../yarn/server/resourcemanager/RMAppManager.java | 9 +- .../resourcemanager/scheduler/SchedulerUtils.java | 11 +- .../scheduler/capacity/AbstractCSQueue.java | 9 + .../scheduler/capacity/CSQueue.java | 10 + .../scheduler/capacity/CapacityScheduler.java | 24 +++ .../capacity/CapacitySchedulerConfiguration.java | 20 +- .../capacity/CapacitySchedulerContext.java | 3 + .../scheduler/capacity/LeafQueue.java | 27 +++ .../capacity/PriorityACLConfiguration.java | 240 +++++++++++++++++++++ .../resourcemanager/security/QueueACLsManager.java | 28 +++ 15 files changed, 436 insertions(+), 6 deletions(-) create mode 100644 hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/records/PriorityACL.java create mode 100644 hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/PriorityACLConfiguration.java diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/records/PriorityACL.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/records/PriorityACL.java new file mode 100644 index 0000000..05c4674 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/records/PriorityACL.java @@ -0,0 +1,40 @@ +/** + * 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.api.records; + +import org.apache.hadoop.classification.InterfaceAudience.Public; +import org.apache.hadoop.classification.InterfaceStability.Evolving; + +/** + * {@code PriorityACL} enumerates ACLs for application priority. + *

+ * The ACL is one of: + *

+ */ +@Public +@Evolving +public enum PriorityACL { + /** + * ACL to submit applications to the queue with given priority. + */ + SUBMIT_APP_PRIORITY, +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/AccessType.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/AccessType.java index 32459b9..3d97b75 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/AccessType.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/AccessType.java @@ -30,4 +30,5 @@ // queue SUBMIT_APP, ADMINISTER_QUEUE, + SUBMIT_APP_PRIORITY } \ No newline at end of file diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/ConfiguredYarnAuthorizer.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/ConfiguredYarnAuthorizer.java index 36c5214..f380298 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/ConfiguredYarnAuthorizer.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/ConfiguredYarnAuthorizer.java @@ -84,8 +84,8 @@ private boolean checkPermissionInternal(AccessType accessType, if (!queueName.contains(".")) { return ret; } - String parentQueueName = - queueName.substring(0, queueName.lastIndexOf(".")); + String parentQueueName = queueName.substring(0, + queueName.lastIndexOf(".")); return checkPermissionInternal(accessType, new PrivilegedEntity(target.getType(), parentQueueName), user); } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/PrivilegedEntity.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/PrivilegedEntity.java index 580bdf4..bfbbbac 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/PrivilegedEntity.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/PrivilegedEntity.java @@ -34,7 +34,8 @@ public class PrivilegedEntity { public enum EntityType { - QUEUE + QUEUE, + PRIORITY } EntityType type; diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ClientRMService.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ClientRMService.java index e9bd230..34d9164 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ClientRMService.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ClientRMService.java @@ -119,6 +119,7 @@ import org.apache.hadoop.yarn.api.records.NodeReport; import org.apache.hadoop.yarn.api.records.NodeState; import org.apache.hadoop.yarn.api.records.Priority; +import org.apache.hadoop.yarn.api.records.PriorityACL; import org.apache.hadoop.yarn.api.records.QueueACL; import org.apache.hadoop.yarn.api.records.QueueInfo; import org.apache.hadoop.yarn.api.records.ReservationACL; @@ -1621,6 +1622,18 @@ public UpdateApplicationPriorityResponse updateApplicationPriority( + ApplicationAccessType.MODIFY_APP.name() + " on " + applicationId)); } + if (!queueACLsManager.checkAccess(callerUGI, + PriorityACL.SUBMIT_APP_PRIORITY, application, newAppPriority)) { + RMAuditLogger.logFailure(callerUGI.getShortUserName(), + AuditConstants.UPDATE_APP_PRIORITY, + "User doesn't have permissions to " + + ApplicationAccessType.MODIFY_APP.toString(), + "ClientRMService", AuditConstants.UNAUTHORIZED_USER, applicationId); + throw RPCUtil.getRemoteException(new AccessControlException("User " + + callerUGI.getShortUserName() + " cannot perform operation " + + ApplicationAccessType.MODIFY_APP.name() + " on " + applicationId)); + } + UpdateApplicationPriorityResponse response = recordFactory .newRecordInstance(UpdateApplicationPriorityResponse.class); // Update priority only when app is tracked by the scheduler diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/RMAppManager.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/RMAppManager.java index c065b60..bfe0f93 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/RMAppManager.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/RMAppManager.java @@ -35,6 +35,7 @@ import org.apache.hadoop.yarn.api.records.ApplicationId; import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext; import org.apache.hadoop.yarn.api.records.Priority; +import org.apache.hadoop.yarn.api.records.PriorityACL; import org.apache.hadoop.yarn.api.records.QueueACL; import org.apache.hadoop.yarn.api.records.ResourceRequest; import org.apache.hadoop.yarn.conf.YarnConfiguration; @@ -368,6 +369,7 @@ private RMAppImpl createAndPopulateNewRMApp( String queueName = submissionContext.getQueue(); String appName = submissionContext.getApplicationName(); CSQueue csqueue = ((CapacityScheduler) scheduler).getQueue(queueName); + Priority priority = submissionContext.getPriority(); if (null != csqueue && !authorizer.checkPermission( new AccessRequest(csqueue.getPrivilegedEntity(), userUgi, @@ -378,7 +380,12 @@ private RMAppImpl createAndPopulateNewRMApp( new AccessRequest(csqueue.getPrivilegedEntity(), userUgi, SchedulerUtils.toAccessType(QueueACL.ADMINISTER_QUEUE), applicationId.toString(), appName, Server.getRemoteAddress(), - null))) { + null)) + && !authorizer.checkPermission(new AccessRequest( + csqueue.getPriorityPrivilegedEntity(priority), userUgi, + SchedulerUtils.toAccessType(PriorityACL.SUBMIT_APP_PRIORITY), + applicationId.toString(), appName, Server.getRemoteAddress(), + null))) { throw RPCUtil.getRemoteException(new AccessControlException( "User " + user + " does not have permission to submit " + applicationId + " to queue " + submissionContext.getQueue())); diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/SchedulerUtils.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/SchedulerUtils.java index c999e26..0b4663d 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/SchedulerUtils.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/SchedulerUtils.java @@ -29,6 +29,7 @@ import org.apache.hadoop.yarn.api.records.ContainerId; import org.apache.hadoop.yarn.api.records.ContainerState; import org.apache.hadoop.yarn.api.records.ContainerStatus; +import org.apache.hadoop.yarn.api.records.PriorityACL; import org.apache.hadoop.yarn.api.records.QueueACL; import org.apache.hadoop.yarn.api.records.QueueInfo; import org.apache.hadoop.yarn.api.records.Resource; @@ -379,7 +380,15 @@ public static AccessType toAccessType(QueueACL acl) { } return null; } - + + public static AccessType toAccessType(PriorityACL acl) { + switch (acl) { + case SUBMIT_APP_PRIORITY: + return AccessType.SUBMIT_APP_PRIORITY; + } + return null; + } + public static boolean checkResourceRequestMatchingNodePartition( String requestedPartition, String nodePartition, SchedulingMode schedulingMode) { diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/AbstractCSQueue.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/AbstractCSQueue.java index 7e18b29..24be36f 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/AbstractCSQueue.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/AbstractCSQueue.java @@ -24,6 +24,7 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; +import java.util.TreeMap; import java.util.concurrent.locks.ReentrantReadWriteLock; import com.google.common.annotations.VisibleForTesting; @@ -105,6 +106,9 @@ protected ReentrantReadWriteLock.ReadLock readLock; protected ReentrantReadWriteLock.WriteLock writeLock; + // All priority ACLs entities have to be stored in a map. + protected Map priorityEntities = new TreeMap<>(); + public AbstractCSQueue(CapacitySchedulerContext cs, String queueName, CSQueue parent, CSQueue old) throws IOException { this.labelManager = cs.getRMContext().getNodeLabelManager(); @@ -216,6 +220,11 @@ public void setParent(CSQueue newParentQueue) { } @Override + public PrivilegedEntity getPriorityPrivilegedEntity(Priority priority) { + return priorityEntities.get(priority); + } + + @Override public boolean hasAccess(QueueACL acl, UserGroupInformation user) { return authorizer.checkPermission( new AccessRequest(queueEntity, user, SchedulerUtils.toAccessType(acl), diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CSQueue.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CSQueue.java index e5cbd04..b4441c7 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CSQueue.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CSQueue.java @@ -31,6 +31,7 @@ import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; import org.apache.hadoop.yarn.api.records.ApplicationId; import org.apache.hadoop.yarn.api.records.ContainerStatus; +import org.apache.hadoop.yarn.api.records.Priority; import org.apache.hadoop.yarn.api.records.QueueACL; import org.apache.hadoop.yarn.api.records.QueueState; import org.apache.hadoop.yarn.api.records.Resource; @@ -355,4 +356,13 @@ boolean accept(Resource cluster, void apply(Resource cluster, ResourceCommitRequest request); + + /** + * Get privileged entity object corresponding to priority. + * + * @param priority + * of the application + * @return privileged entity object corresponding to priority + */ + public PrivilegedEntity getPriorityPrivilegedEntity(Priority priority); } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacityScheduler.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacityScheduler.java index 7e98f10..dab4df8 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacityScheduler.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacityScheduler.java @@ -47,6 +47,7 @@ import org.apache.hadoop.security.AccessControlException; import org.apache.hadoop.security.Groups; import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.security.authorize.AccessControlList; import org.apache.hadoop.util.Time; import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; import org.apache.hadoop.yarn.api.records.ApplicationId; @@ -68,6 +69,7 @@ import org.apache.hadoop.yarn.exceptions.YarnException; import org.apache.hadoop.yarn.exceptions.YarnRuntimeException; import org.apache.hadoop.yarn.proto.YarnServiceProtos.SchedulerResourceTypes; +import org.apache.hadoop.yarn.security.AccessType; import org.apache.hadoop.yarn.security.Permission; import org.apache.hadoop.yarn.security.YarnAuthorizationProvider; import org.apache.hadoop.yarn.server.resourcemanager.RMContext; @@ -622,6 +624,7 @@ private void initializeQueues(CapacitySchedulerConfiguration conf) LOG.info("Initialized root queue " + root); updatePlacementRules(); setQueueAcls(authorizer, queues); + setPriorityAcls(authorizer, queues); // Notify Preemption Manager preemptionManager.refreshQueues(null, root); @@ -653,6 +656,7 @@ private void reinitializeQueues(CapacitySchedulerConfiguration newConf) labelManager.reinitializeQueueLabels(getQueueToLabels()); setQueueAcls(authorizer, queues); + setPriorityAcls(authorizer, queues); // Notify Preemption Manager preemptionManager.refreshQueues(null, root); @@ -670,6 +674,26 @@ public static void setQueueAcls(YarnAuthorizationProvider authorizer, authorizer.setPermission(permissions, UserGroupInformation.getCurrentUser()); } + public static void setPriorityAcls(YarnAuthorizationProvider authorizer, + Map queues) throws IOException { + List permissions = new ArrayList<>(); + for (CSQueue queue : queues.values()) { + if (queue instanceof LeafQueue) { + LeafQueue lQueue = (LeafQueue) queue; + + for (Entry entry : lQueue.getPriorityACLs() + .entrySet()) { + Map acl = new HashMap<>(); + acl.put(AccessType.SUBMIT_APP_PRIORITY, entry.getValue()); + permissions.add( + new Permission(lQueue.getPriorityPrivilegedEntity(entry.getKey()), acl)); + } + } + } + authorizer.setPermission(permissions, + UserGroupInformation.getCurrentUser()); + } + private Map> getQueueToLabels() { Map> queueToLabels = new HashMap>(); for (CSQueue queue : queues.values()) { diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacitySchedulerConfiguration.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacitySchedulerConfiguration.java index c153c26..5887e58 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacitySchedulerConfiguration.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacitySchedulerConfiguration.java @@ -38,6 +38,8 @@ import org.apache.hadoop.security.authorize.AccessControlList; import org.apache.hadoop.util.ReflectionUtils; import org.apache.hadoop.util.StringUtils; +import org.apache.hadoop.yarn.api.records.Priority; +import org.apache.hadoop.yarn.api.records.PriorityACL; import org.apache.hadoop.yarn.api.records.QueueACL; import org.apache.hadoop.yarn.api.records.QueueState; import org.apache.hadoop.yarn.api.records.ReservationACL; @@ -63,7 +65,7 @@ private static final Log LOG = LogFactory.getLog(CapacitySchedulerConfiguration.class); - + private static final String CS_CONFIGURATION_FILE = "capacity-scheduler.xml"; @Private @@ -274,6 +276,8 @@ @Private public static final boolean DEFAULT_LAZY_PREEMPTION_ENABLED = false; + PriorityACLConfiguration priorityACLConfig = new PriorityACLConfiguration(); + public CapacitySchedulerConfiguration() { this(new Configuration()); } @@ -588,6 +592,10 @@ private static String getAclKey(ReservationACL acl) { return "acl_" + StringUtils.toLowerCase(acl.toString()); } + private static String getAclKey(PriorityACL acl) { + return "acl_" + StringUtils.toLowerCase(acl.toString()); + } + @Override public Map getReservationAcls(String queue) { @@ -636,6 +644,16 @@ public void setReservationAcls(String queue, } } + public Map getPriorityAcls(String queue, + Priority clusterMaxPriority) { + String queuePrefix = getQueuePrefix(queue); + String defaultAcl = ALL_ACL; + String aclString = get( + queuePrefix + getAclKey(PriorityACL.SUBMIT_APP_PRIORITY), defaultAcl); + + return priorityACLConfig.getPrioirityAcl(clusterMaxPriority, aclString); + } + public String[] getQueues(String queue) { LOG.debug("CSConf - getQueues called for: queuePrefix=" + getQueuePrefix(queue)); String[] queues = getStrings(getQueuePrefix(queue) + QUEUES); diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacitySchedulerContext.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacitySchedulerContext.java index c41a7bf..1a52eda 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacitySchedulerContext.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacitySchedulerContext.java @@ -23,6 +23,7 @@ import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; import org.apache.hadoop.yarn.api.records.NodeId; +import org.apache.hadoop.yarn.api.records.Priority; import org.apache.hadoop.yarn.api.records.Resource; import org.apache.hadoop.yarn.server.resourcemanager.RMContext; import org.apache.hadoop.yarn.server.resourcemanager.scheduler.activities.ActivitiesManager; @@ -83,4 +84,6 @@ ResourceUsage getClusterResourceUsage(); ActivitiesManager getActivitiesManager(); + + Priority getMaxClusterLevelAppPriority(); } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/LeafQueue.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/LeafQueue.java index 161957f..e1d3762 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/LeafQueue.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/LeafQueue.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.util.*; +import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; @@ -40,6 +41,7 @@ import org.apache.hadoop.yarn.api.records.ContainerExitStatus; import org.apache.hadoop.yarn.api.records.ContainerStatus; import org.apache.hadoop.yarn.api.records.Priority; +import org.apache.hadoop.yarn.api.records.PriorityACL; import org.apache.hadoop.yarn.api.records.QueueACL; import org.apache.hadoop.yarn.api.records.QueueInfo; import org.apache.hadoop.yarn.api.records.QueueState; @@ -50,6 +52,8 @@ import org.apache.hadoop.yarn.factory.providers.RecordFactoryProvider; import org.apache.hadoop.yarn.nodelabels.CommonNodeLabelsManager; import org.apache.hadoop.yarn.security.AccessType; +import org.apache.hadoop.yarn.security.PrivilegedEntity; +import org.apache.hadoop.yarn.security.PrivilegedEntity.EntityType; import org.apache.hadoop.yarn.server.resourcemanager.RMServerUtils; import org.apache.hadoop.yarn.server.resourcemanager.nodelabels.RMNodeLabelsManager; import org.apache.hadoop.yarn.server.resourcemanager.rmcontainer.RMContainer; @@ -139,6 +143,9 @@ private Map> ignorePartitionExclusivityRMContainers = new ConcurrentHashMap<>(); + Map priorityAcls = + new HashMap(); + @SuppressWarnings({ "unchecked", "rawtypes" }) public LeafQueue(CapacitySchedulerContext cs, String queueName, CSQueue parent, CSQueue old) throws IOException { @@ -199,6 +206,16 @@ protected void setupQueueConfigs(Resource clusterResource) conf.getMaximumApplicationMasterResourcePerQueuePercent( getQueuePath()); + priorityAcls = conf.getPriorityAcls(getQueuePath(), + scheduler.getMaxClusterLevelAppPriority()); + for (Entry entry : priorityAcls.entrySet()) { + Priority priority = entry.getKey(); + StringBuilder str = new StringBuilder(); + str.append(priority.getPriority()); + priorityEntities.put(priority, + new PrivilegedEntity(EntityType.PRIORITY, str.toString())); + } + if (!SchedulerUtils.checkQueueLabelExpression(this.accessibleLabels, this.defaultLabelExpression, null)) { throw new IOException( @@ -491,6 +508,16 @@ private User getUserAndAddIfAbsent(String userName) { } } + @Private + public Map getPriorityACLs() { + try { + readLock.lock(); + return priorityAcls; + } finally { + readLock.unlock(); + } + } + @Override public void reinitialize( CSQueue newlyParsedQueue, Resource clusterResource) diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/PriorityACLConfiguration.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/PriorityACLConfiguration.java new file mode 100644 index 0000000..a0b7eb9 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/PriorityACLConfiguration.java @@ -0,0 +1,240 @@ +/** +* 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.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.hadoop.classification.InterfaceAudience.Private; +import org.apache.hadoop.security.authorize.AccessControlList; +import org.apache.hadoop.util.StringUtils; +import org.apache.hadoop.yarn.api.records.Priority; + +public class PriorityACLConfiguration { + + public enum PriorityACLConfig { + USER(1), GROUP(2), MAX_PRIORITY(3), DEFAULT_PRIORITY(4); + + private final int id; + + PriorityACLConfig(int id) { + this.id = id; + } + + public int getId() { + return this.id; + } + } + + public static final String PATTERN_FOR_PRIORITY_ACL = "\\[([^\\]]+)"; + + @Private + public static final String ALL_ACL = "*"; + + @Private + public static final String NONE_ACL = " "; + + public HashMap getPrioirityAcl( + Priority clusterMaxPriority, String aclString) { + + Map> map = new HashMap<>(); + Matcher matcher = Pattern.compile(PATTERN_FOR_PRIORITY_ACL) + .matcher(aclString); + + /* + * Each ACL group will be separated by "[]". Syntax of each ACL group could + * be like below "max-priority=a1 user=b1,b2 default-priority=c1" + */ + while (matcher.find()) { + // Get the first ACL sub-group. + String aclSubGroup = matcher.group(1); + if (aclSubGroup.trim().isEmpty()) { + continue; + } + + /* + * Internal tracking storage (map) needs key as 'priority' and value as + * ' '. So save priority after initial parsing so that + * for USER or GROUP parsing and storage, user saved priority as key. + */ + Priority max_priority = null; + for (String kvPair : aclSubGroup.trim().split(" +")) { + /* + * There are 3 possible options for key here: 1. user/group 2. + * max-priority 3. default-priority + * + * Categorize ACL map with max-priority as key and keep acl string as + * "user1,user2 group" format. + */ + String[] splits = kvPair.split("="); + + // Ensure that each ACL sub string is key value pair separated by '='. + if (splits != null && splits.length > 1) { + max_priority = parsePriorityACLType(map, max_priority, splits); + } + } + } + + // Consider default case where no ACLs are configured and also case where + // ACLs to be added till cluster max priority. + handleDefaultConfigForACLs(map, aclString, clusterMaxPriority); + + // Finally output map will have priority as key. Here value is acl string. + // Value could be a join of user and group. + HashMap output = new HashMap<>(); + createACLStringPerPriority(output, map); + + return output; + } + + /* + * This method will help to append different types of ACLs keys against one + * priority. For eg,USER will be appended with GROUP as "user2,user4 group1". + */ + private void createACLStringPerPriority( + HashMap output, + Map> map) { + for (Entry> entry : map.entrySet()) { + + Priority priority = entry.getKey(); + List acls = entry.getValue(); + + String finalACL = new String(); + // If any of user/group is *, consider it as acceptable for all. + if (acls.get(0).equals(ALL_ACL) || acls.get(1).equals(ALL_ACL)) { + finalACL = ALL_ACL; + } else { + // skip last appended "," + String user = acls.get(0).substring(0, acls.get(0).length() - 2); + String group = acls.get(1).substring(0, acls.get(1).length() - 2); + finalACL = user + " " + group; + } + + output.put(priority, new AccessControlList(finalACL.trim())); + } + } + + /* + * Parse different types of ACLs sub parts for on priority group and store in + * a map for later processing. + */ + private Priority parsePriorityACLType(Map> map, + Priority max_priority, String[] splits) { + // Here splits will have the key value pair at index 0 and 1 respectively. + // To parse all keys, its better to convert to PriorityACLConfig enum. + PriorityACLConfig aclType = PriorityACLConfig + .valueOf(StringUtils.toUpperCase(splits[0].trim())); + switch (aclType) { + case MAX_PRIORITY : + max_priority = Priority.newInstance(Integer.parseInt(splits[1])); + break; + case USER : + createACLStringForPriority(map, max_priority, splits[1], + PriorityACLConfig.USER); + break; + case GROUP : + createACLStringForPriority(map, max_priority, splits[1], + PriorityACLConfig.GROUP); + break; + case DEFAULT_PRIORITY : + // ToDO + break; + } + return max_priority; + } + + /* + * This method will help to append user/group acl string against given + * priority. For example "user1,user2 group1,group2" + */ + private void createACLStringForPriority( + Map> map, Priority max_priority, + String value, PriorityACLConfig type) { + + // Ideally max_priority shouldn't be null. + if (null == max_priority) { + return; + } + + // Fill ACL for each possible priority entries. + for (int i = 0; i <= max_priority.getPriority(); i++) { + List aclList = map.get(Priority.newInstance(i)); + if (aclList == null) { + aclList = new ArrayList<>(); + aclList.add(new StringBuilder()); + aclList.add(new StringBuilder()); + map.put(max_priority, aclList); + } + + // ACL strings could be generate for USER or GRUOP. + // aclList in map contains two entries. 1. USER, 2. GROUP. + StringBuilder aclTypeName = aclList.get(type.getId() - 1); + if (aclTypeName.equals(ALL_ACL)) { + return; + } + + if (value.trim().equals(ALL_ACL)) { + aclTypeName.setLength(0); + aclTypeName.append(ALL_ACL); + return; + } + + // Append a space as delim. + aclTypeName.append(value.trim()).append(","); + } + } + + private void handleDefaultConfigForACLs( + Map> map, String aclString, + Priority clusterMaxPriority) { + // If there are no configurations for ACL, then ALL_ACL can be + // set for all priorities. + if (map.isEmpty()) { + setDefaultACLTillConfiguredPriority(map, 0, + clusterMaxPriority.getPriority(), ALL_ACL); + } else { + // Find the max priority to which ACLs are configured. Remaining + // priorities till Cluster max priority should be set as NONE_ACL. + int configuredMaxPriority = 0; + for (Entry> entry : map.entrySet()) { + if (entry.getKey().getPriority() > configuredMaxPriority) { + configuredMaxPriority = entry.getKey().getPriority(); + } + } + setDefaultACLTillConfiguredPriority(map, configuredMaxPriority + 1, + clusterMaxPriority.getPriority(), NONE_ACL); + } + } + + private void setDefaultACLTillConfiguredPriority( + Map> map, int fromThisPriority, + int tillThisPriority, String acl) { + for (int i = fromThisPriority; i <= tillThisPriority; i++) { + Priority priority = Priority.newInstance(i); + List aclList = new ArrayList<>(); + aclList.add(new StringBuilder(acl)); + map.put(priority, aclList); + } + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/security/QueueACLsManager.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/security/QueueACLsManager.java index c9d55f1..3104a3a 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/security/QueueACLsManager.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/security/QueueACLsManager.java @@ -22,7 +22,10 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.ipc.Server; import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.yarn.api.records.Priority; +import org.apache.hadoop.yarn.api.records.PriorityACL; import org.apache.hadoop.yarn.api.records.QueueACL; import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.security.AccessRequest; @@ -80,4 +83,29 @@ public boolean checkAccess(UserGroupInformation callerUGI, QueueACL acl, return scheduler.checkAccess(callerUGI, acl, app.getQueue()); } } + + public boolean checkAccess(UserGroupInformation callerUGI, PriorityACL acl, + RMApp app, Priority newAppPriority) { + if (!isACLsEnable) { + return true; + } + + if (scheduler instanceof CapacityScheduler) { + CSQueue queue = ((CapacityScheduler) scheduler).getQueue(app.getQueue()); + if (queue == null) { + // Application exists but the associated queue does not exist. + // This may be due to queue is removed after RM restarts. Here, we + // choose to allow users to be able to view the apps for removed queue. + LOG.error("Queue " + app.getQueue() + " does not exist for " + + app.getApplicationId()); + return true; + } + return authorizer.checkPermission(new AccessRequest( + queue.getPriorityPrivilegedEntity(newAppPriority), callerUGI, + SchedulerUtils.toAccessType(acl), app.getApplicationId().toString(), + app.getName(), Server.getRemoteAddress(), null)); + } + + return true; + } } -- 2.7.4 (Apple Git-66)