blob: 4489a01b69d2b299ba0db6e843596dd2132a31d7 [file] [log] [blame]
/**
* 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.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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;
/**
*
* PriorityACLConfiguration class is used to parse Application Priority ACL
* configuration from capcity-scheduler.xml
*/
public class AppPriorityACLConfigurationParser {
private static final Log LOG = LogFactory
.getLog(AppPriorityACLConfigurationParser.class);
public enum AppPriorityACLKeyType {
USER(1), GROUP(2), MAX_PRIORITY(3), DEFAULT_PRIORITY(4);
private final int id;
AppPriorityACLKeyType(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 List<AppPriorityACLGroup> getPriorityAcl(Priority clusterMaxPriority,
String aclString) {
List<AppPriorityACLGroup> aclList = new ArrayList<AppPriorityACLGroup>();
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 "user=b1,b2 group=g1 max-priority=a2 default-priority=a1"
* Ideally this means "for this given user/group, maximum possible priority
* is a2 and if the user has not specified any priority, then it is a1."
*/
while (matcher.find()) {
// Get the first ACL sub-group.
String aclSubGroup = matcher.group(1);
if (aclSubGroup.trim().isEmpty()) {
continue;
}
/*
* Internal storage is PriorityACLGroup which stores each parsed priority
* ACLs group. This will help while looking for a user to priority mapping
* during app submission time. ACLs will be passed in below order only. 1.
* user/group 2. max-priority 3. default-priority
*/
AppPriorityACLGroup userPriorityACL = new AppPriorityACLGroup();
// userAndGroupName will hold user acl and group acl as interim storage
// since both user/group acl comes with separate key value pairs.
List<StringBuilder> userAndGroupName = new ArrayList<>();
for (String kvPair : aclSubGroup.trim().split(" +")) {
/*
* There are 3 possible options for key here: 1. user/group 2.
* max-priority 3. default-priority
*/
String[] splits = kvPair.split("=");
// Ensure that each ACL sub string is key value pair separated by '='.
if (splits != null && splits.length > 1) {
parsePriorityACLType(userPriorityACL, splits, userAndGroupName);
}
}
// If max_priority is higher to clusterMaxPriority, its better to
// handle here.
if (userPriorityACL.getMaxPriority().getPriority() > clusterMaxPriority
.getPriority()) {
LOG.warn("ACL configuration for '" + userPriorityACL.getMaxPriority()
+ "' is greater that cluster max priority. Resetting ACLs to "
+ clusterMaxPriority);
userPriorityACL.setMaxPriority(
Priority.newInstance(clusterMaxPriority.getPriority()));
}
AccessControlList acl = createACLStringForPriority(userAndGroupName);
userPriorityACL.setACLList(acl);
aclList.add(userPriorityACL);
}
return aclList;
}
/*
* Parse different types of ACLs sub parts for on priority group and store in
* a map for later processing.
*/
private void parsePriorityACLType(AppPriorityACLGroup userPriorityACL,
String[] splits, List<StringBuilder> userAndGroupName) {
// 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.
AppPriorityACLKeyType aclType = AppPriorityACLKeyType
.valueOf(StringUtils.toUpperCase(splits[0].trim()));
switch (aclType) {
case MAX_PRIORITY :
userPriorityACL
.setMaxPriority(Priority.newInstance(Integer.parseInt(splits[1])));
break;
case USER :
userAndGroupName.add(getUserOrGroupACLStringFromConfig(splits[1]));
break;
case GROUP :
userAndGroupName.add(getUserOrGroupACLStringFromConfig(splits[1]));
break;
case DEFAULT_PRIORITY :
int defaultPriority = Integer.parseInt(splits[1]);
Priority priority = (defaultPriority < 0)
? Priority.newInstance(0)
: Priority.newInstance(defaultPriority);
userPriorityACL.setDefaultPriority(priority);
break;
default:
break;
}
}
/*
* 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 AccessControlList createACLStringForPriority(
List<StringBuilder> acls) {
String finalACL = "";
String userACL = acls.get(0).toString();
// If any of user/group is *, consider it as acceptable for all.
// "user" is at index 0, and "group" is at index 1.
if (userACL.trim().equals(ALL_ACL)) {
finalACL = ALL_ACL;
} else if (userACL.equals(NONE_ACL)) {
finalACL = NONE_ACL;
} else {
// Get USER segment
if (!userACL.trim().isEmpty()) {
// skip last appended ","
finalACL = acls.get(0).toString();
}
// Get GROUP segment if any
if (acls.size() > 1) {
String groupACL = acls.get(1).toString();
if (!groupACL.trim().isEmpty()) {
finalACL = finalACL + " "
+ acls.get(1).toString();
}
}
}
// Here ACL will look like "user1,user2 group" in ideal cases.
return new AccessControlList(finalACL.trim());
}
/*
* This method will help to append user/group acl string against given
* priority. For example "user1,user2 group1,group2"
*/
private StringBuilder getUserOrGroupACLStringFromConfig(String value) {
// ACL strings could be generate for USER or GRUOP.
// aclList in map contains two entries. 1. USER, 2. GROUP.
StringBuilder aclTypeName = new StringBuilder();
if (value.trim().equals(ALL_ACL)) {
aclTypeName.setLength(0);
aclTypeName.append(ALL_ACL);
return aclTypeName;
}
aclTypeName.append(value.trim());
return aclTypeName;
}
}