You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by ju...@apache.org on 2015/04/14 03:18:30 UTC

hadoop git commit: YARN-3443. Create a 'ResourceHandler' subsystem to ease addition of support for new resource types on the NM. Contributed by Sidharta Seethana.

Repository: hadoop
Updated Branches:
  refs/heads/trunk a77d62833 -> 838b06ac8


YARN-3443. Create a 'ResourceHandler' subsystem to ease addition of support for new resource types on the NM. Contributed by Sidharta Seethana.


Project: http://git-wip-us.apache.org/repos/asf/hadoop/repo
Commit: http://git-wip-us.apache.org/repos/asf/hadoop/commit/838b06ac
Tree: http://git-wip-us.apache.org/repos/asf/hadoop/tree/838b06ac
Diff: http://git-wip-us.apache.org/repos/asf/hadoop/diff/838b06ac

Branch: refs/heads/trunk
Commit: 838b06ac87339494cea706d4a97e7f5383bdc442
Parents: a77d628
Author: Junping Du <ju...@apache.org>
Authored: Mon Apr 13 18:35:56 2015 -0700
Committer: Junping Du <ju...@apache.org>
Committed: Mon Apr 13 18:35:56 2015 -0700

----------------------------------------------------------------------
 hadoop-yarn-project/CHANGES.txt                 |   3 +
 .../linux/privileged/PrivilegedOperation.java   | 119 +++++
 .../PrivilegedOperationException.java           |  43 ++
 .../privileged/PrivilegedOperationExecutor.java | 255 +++++++++++
 .../linux/resources/CGroupsHandler.java         | 132 ++++++
 .../linux/resources/CGroupsHandlerImpl.java     | 436 +++++++++++++++++++
 .../linux/resources/ResourceHandler.java        |  91 ++++
 .../linux/resources/ResourceHandlerChain.java   | 142 ++++++
 .../resources/ResourceHandlerException.java     |  47 ++
 .../TestPrivilegedOperationExecutor.java        | 233 ++++++++++
 .../linux/resources/TestCGroupsHandlerImpl.java | 235 ++++++++++
 11 files changed, 1736 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hadoop/blob/838b06ac/hadoop-yarn-project/CHANGES.txt
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/CHANGES.txt b/hadoop-yarn-project/CHANGES.txt
index 11fdf39..bec5ce1 100644
--- a/hadoop-yarn-project/CHANGES.txt
+++ b/hadoop-yarn-project/CHANGES.txt
@@ -69,6 +69,9 @@ Release 2.8.0 - UNRELEASED
     YARN-3347. Improve YARN log command to get AMContainer logs as well as 
     running containers logs. (Xuan Gong via junping_du)
 
+    YARN-3443. Create a 'ResourceHandler' subsystem to ease addition of support 
+    for new resource types on the NM. (Sidharta Seethana via junping_du)
+
   IMPROVEMENTS
 
     YARN-1880. Cleanup TestApplicationClientProtocolOnHA

http://git-wip-us.apache.org/repos/asf/hadoop/blob/838b06ac/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/privileged/PrivilegedOperation.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/privileged/PrivilegedOperation.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/privileged/PrivilegedOperation.java
new file mode 100644
index 0000000..74556a8
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/privileged/PrivilegedOperation.java
@@ -0,0 +1,119 @@
+/*
+ * *
+ *  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.nodemanager.containermanager.linux.privileged;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Represents operations that require higher system privileges - e.g
+ * creating cgroups, launching containers as specified users, 'tc' commands etc
+ * that are completed using the container-executor binary
+ */
+
+@InterfaceAudience.Private
+@InterfaceStability.Unstable
+public class PrivilegedOperation {
+
+  public enum OperationType {
+    CHECK_SETUP("--checksetup"),
+    MOUNT_CGROUPS("--mount-cgroups"),
+    INITIALIZE_CONTAINER(""), //no CLI switch supported yet
+    LAUNCH_CONTAINER(""), //no CLI switch supported yet
+    SIGNAL_CONTAINER(""), //no CLI switch supported yet
+    DELETE_AS_USER(""), //no CLI switch supported yet
+    TC_MODIFY_STATE("--tc-modify-state"),
+    TC_READ_STATE("--tc-read-state"),
+    TC_READ_STATS("--tc-read-stats"),
+    ADD_PID_TO_CGROUP(""); //no CLI switch supported yet.
+
+    private final String option;
+
+    OperationType(String option) {
+      this.option = option;
+    }
+
+    public String getOption() {
+      return option;
+    }
+  }
+
+  public static final String CGROUP_ARG_PREFIX = "cgroups=";
+
+  private final OperationType opType;
+  private final List<String> args;
+
+  public PrivilegedOperation(OperationType opType, String arg) {
+    this.opType = opType;
+    this.args = new ArrayList<String>();
+
+    if (arg != null) {
+      this.args.add(arg);
+    }
+  }
+
+  public PrivilegedOperation(OperationType opType, List<String> args) {
+    this.opType = opType;
+    this.args = new ArrayList<String>();
+
+    if (args != null) {
+      this.args.addAll(args);
+    }
+  }
+
+  public void appendArgs(String... args) {
+    for (String arg : args) {
+      this.args.add(arg);
+    }
+  }
+
+  public void appendArgs(List<String> args) {
+    this.args.addAll(args);
+  }
+
+  public OperationType getOperationType() {
+    return opType;
+  }
+
+  public List<String> getArguments() {
+    return Collections.unmodifiableList(this.args);
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (other == null || !(other instanceof PrivilegedOperation)) {
+      return false;
+    }
+
+    PrivilegedOperation otherOp = (PrivilegedOperation) other;
+
+    return otherOp.opType.equals(opType) && otherOp.args.equals(args);
+  }
+
+  @Override
+  public int hashCode() {
+    return opType.hashCode() + 97 * args.hashCode();
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/hadoop/blob/838b06ac/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/privileged/PrivilegedOperationException.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/privileged/PrivilegedOperationException.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/privileged/PrivilegedOperationException.java
new file mode 100644
index 0000000..20c234d
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/privileged/PrivilegedOperationException.java
@@ -0,0 +1,43 @@
+/*
+ * *
+ *  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.nodemanager.containermanager.linux.privileged;
+
+import org.apache.hadoop.yarn.exceptions.YarnException;
+
+public class PrivilegedOperationException extends YarnException {
+  private static final long serialVersionUID = 1L;
+
+  public PrivilegedOperationException() {
+    super();
+  }
+
+  public PrivilegedOperationException(String message) {
+    super(message);
+  }
+
+  public PrivilegedOperationException(Throwable cause) {
+    super(cause);
+  }
+
+  public PrivilegedOperationException(String message, Throwable cause) {
+    super(message, cause);
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/838b06ac/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/privileged/PrivilegedOperationExecutor.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/privileged/PrivilegedOperationExecutor.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/privileged/PrivilegedOperationExecutor.java
new file mode 100644
index 0000000..1c4a51c
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/privileged/PrivilegedOperationExecutor.java
@@ -0,0 +1,255 @@
+/*
+ * *
+ *  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.nodemanager.containermanager.linux.privileged;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.util.Shell.ShellCommandExecutor;
+import org.apache.hadoop.util.Shell.ExitCodeException;
+import org.apache.hadoop.yarn.api.ApplicationConstants;
+import org.apache.hadoop.yarn.conf.YarnConfiguration;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * provides mechanisms to execute PrivilegedContainerOperations *
+ */
+
+@InterfaceAudience.Private
+@InterfaceStability.Unstable
+public class PrivilegedOperationExecutor {
+  private static final Log LOG = LogFactory.getLog(PrivilegedOperationExecutor
+      .class);
+  private volatile static PrivilegedOperationExecutor instance;
+
+  private String containerExecutorExe;
+
+  public static String getContainerExecutorExecutablePath(Configuration conf) {
+    String yarnHomeEnvVar =
+        System.getenv(ApplicationConstants.Environment.HADOOP_YARN_HOME.key());
+    File hadoopBin = new File(yarnHomeEnvVar, "bin");
+    String defaultPath =
+        new File(hadoopBin, "container-executor").getAbsolutePath();
+    return null == conf
+        ? defaultPath
+        : conf.get(YarnConfiguration.NM_LINUX_CONTAINER_EXECUTOR_PATH,
+        defaultPath);
+  }
+
+  private void init(Configuration conf) {
+    containerExecutorExe = getContainerExecutorExecutablePath(conf);
+  }
+
+  private PrivilegedOperationExecutor(Configuration conf) {
+    init(conf);
+  }
+
+  public static PrivilegedOperationExecutor getInstance(Configuration conf) {
+    if (instance == null) {
+      synchronized (PrivilegedOperationExecutor.class) {
+        if (instance == null) {
+          instance = new PrivilegedOperationExecutor(conf);
+        }
+      }
+    }
+
+    return instance;
+  }
+
+  /**
+   * @param prefixCommands in some cases ( e.g priorities using nice ),
+   *                       prefix commands are necessary
+   * @param operation      the type and arguments for the operation to be
+   *                       executed
+   * @return execution string array for priviledged operation
+   */
+
+  public String[] getPrivilegedOperationExecutionCommand(List<String>
+      prefixCommands,
+      PrivilegedOperation operation) {
+    List<String> fullCommand = new ArrayList<String>();
+
+    if (prefixCommands != null && !prefixCommands.isEmpty()) {
+      fullCommand.addAll(prefixCommands);
+    }
+
+    fullCommand.add(containerExecutorExe);
+    fullCommand.add(operation.getOperationType().getOption());
+    fullCommand.addAll(operation.getArguments());
+
+    String[] fullCommandArray =
+        fullCommand.toArray(new String[fullCommand.size()]);
+
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Privileged Execution Command Array: " +
+          Arrays.toString(fullCommandArray));
+    }
+
+    return fullCommandArray;
+  }
+
+  /**
+   * Executes a privileged operation. It is up to the callers to ensure that
+   * each privileged operation's parameters are constructed correctly. The
+   * parameters are passed verbatim to the container-executor binary.
+   *
+   * @param prefixCommands in some cases ( e.g priorities using nice ),
+   *                       prefix commands are necessary
+   * @param operation      the type and arguments for the operation to be executed
+   * @param workingDir     (optional) working directory for execution
+   * @param env            (optional) env of the command will include specified vars
+   * @param grabOutput     return (possibly large) shell command output
+   * @return stdout contents from shell executor - useful for some privileged
+   * operations - e.g --tc_read
+   * @throws org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperationException
+   */
+  public String executePrivilegedOperation(List<String> prefixCommands,
+      PrivilegedOperation operation, File workingDir,
+      Map<String, String> env, boolean grabOutput)
+      throws PrivilegedOperationException {
+    String[] fullCommandArray = getPrivilegedOperationExecutionCommand
+        (prefixCommands, operation);
+    ShellCommandExecutor exec = new ShellCommandExecutor(fullCommandArray,
+        workingDir, env);
+
+    try {
+      exec.execute();
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("Privileged Execution Operation Output:");
+        LOG.debug(exec.getOutput());
+      }
+    } catch (ExitCodeException e) {
+      String logLine = new StringBuffer("Shell execution returned exit code: ")
+          .append(exec.getExitCode())
+          .append(". Privileged Execution Operation Output: ")
+          .append(System.lineSeparator()).append(exec.getOutput()).toString();
+
+      LOG.warn(logLine);
+      throw new PrivilegedOperationException(e);
+    } catch (IOException e) {
+      LOG.warn("IOException executing command: ", e);
+      throw new PrivilegedOperationException(e);
+    }
+
+    if (grabOutput) {
+      return exec.getOutput();
+    }
+
+    return null;
+  }
+
+  /**
+   * Executes a privileged operation. It is up to the callers to ensure that
+   * each privileged operation's parameters are constructed correctly. The
+   * parameters are passed verbatim to the container-executor binary.
+   *
+   * @param operation  the type and arguments for the operation to be executed
+   * @param grabOutput return (possibly large) shell command output
+   * @return stdout contents from shell executor - useful for some privileged
+   * operations - e.g --tc_read
+   * @throws org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperationException
+   */
+  public String executePrivilegedOperation(PrivilegedOperation operation,
+      boolean grabOutput) throws PrivilegedOperationException {
+    return executePrivilegedOperation(null, operation, null, null, grabOutput);
+  }
+
+  //Utility functions for squashing together operations in supported ways
+  //At some point, we need to create a generalized mechanism that uses a set
+  //of squashing 'rules' to squash an set of PrivilegedOperations of varying
+  //types - e.g Launch Container + Add Pid to CGroup(s) + TC rules
+
+  /**
+   * Squash operations for cgroups - e.g mount, add pid to cgroup etc .,
+   * For now, we only implement squashing for 'add pid to cgroup' since this
+   * is the only optimization relevant to launching containers
+   *
+   * @return single squashed cgroup operation. Null on failure.
+   */
+
+  public static PrivilegedOperation squashCGroupOperations
+  (List<PrivilegedOperation> ops) throws PrivilegedOperationException {
+    if (ops.size() == 0) {
+      return null;
+    }
+
+    StringBuffer finalOpArg = new StringBuffer(PrivilegedOperation
+        .CGROUP_ARG_PREFIX);
+    boolean noneArgsOnly = true;
+
+    for (PrivilegedOperation op : ops) {
+      if (!op.getOperationType()
+          .equals(PrivilegedOperation.OperationType.ADD_PID_TO_CGROUP)) {
+        LOG.warn("Unsupported operation type: " + op.getOperationType());
+        throw new PrivilegedOperationException("Unsupported operation type:"
+            + op.getOperationType());
+      }
+
+      List<String> args = op.getArguments();
+      if (args.size() != 1) {
+        LOG.warn("Invalid number of args: " + args.size());
+        throw new PrivilegedOperationException("Invalid number of args: "
+            + args.size());
+      }
+
+      String arg = args.get(0);
+      String tasksFile = StringUtils.substringAfter(arg,
+          PrivilegedOperation.CGROUP_ARG_PREFIX);
+      if (tasksFile == null || tasksFile.isEmpty()) {
+        LOG.warn("Invalid argument: " + arg);
+        throw new PrivilegedOperationException("Invalid argument: " + arg);
+      }
+
+      if (tasksFile.equals("none")) {
+        //Don't append to finalOpArg
+        continue;
+      }
+
+      if (noneArgsOnly == false) {
+        //We have already appended at least one tasks file.
+        finalOpArg.append(",");
+        finalOpArg.append(tasksFile);
+      } else {
+        finalOpArg.append(tasksFile);
+        noneArgsOnly = false;
+      }
+    }
+
+    if (noneArgsOnly) {
+      finalOpArg.append("none"); //there were no tasks file to append
+    }
+
+    PrivilegedOperation finalOp = new PrivilegedOperation(
+        PrivilegedOperation.OperationType.ADD_PID_TO_CGROUP, finalOpArg
+        .toString());
+
+    return finalOp;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/hadoop/blob/838b06ac/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/CGroupsHandler.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/CGroupsHandler.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/CGroupsHandler.java
new file mode 100644
index 0000000..34429d3
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/CGroupsHandler.java
@@ -0,0 +1,132 @@
+/*
+ * *
+ *  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.nodemanager.containermanager.linux.resources;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+
+/**
+ * Provides CGroups functionality. Implementations are expected to be
+ * thread-safe
+ */
+
+@InterfaceAudience.Private
+@InterfaceStability.Unstable
+public interface CGroupsHandler {
+  public enum CGroupController {
+    CPU("cpu"),
+    NET_CLS("net_cls");
+
+    private final String name;
+
+    CGroupController(String name) {
+      this.name = name;
+    }
+
+    String getName() {
+      return name;
+    }
+  }
+
+  public static final String CGROUP_FILE_TASKS = "tasks";
+  public static final String CGROUP_PARAM_CLASSID = "classid";
+
+  /**
+   * Mounts a cgroup controller
+   * @param controller - the controller being mounted
+   * @throws ResourceHandlerException
+   */
+  public void mountCGroupController(CGroupController controller)
+      throws ResourceHandlerException;
+
+  /**
+   * Creates a cgroup for a given controller
+   * @param controller - controller type for which the cgroup is being created
+   * @param cGroupId - id of the cgroup being created
+   * @return full path to created cgroup
+   * @throws ResourceHandlerException
+   */
+  public String createCGroup(CGroupController controller, String cGroupId)
+      throws ResourceHandlerException;
+
+  /**
+   * Deletes the specified cgroup
+   * @param controller - controller type for the cgroup
+   * @param cGroupId - id of the cgroup being deleted
+   * @throws ResourceHandlerException
+   */
+  public void deleteCGroup(CGroupController controller, String cGroupId) throws
+      ResourceHandlerException;
+
+  /**
+   * Gets the full path for the cgroup, given a controller and a cgroup id
+   * @param controller - controller type for the cgroup
+   * @param cGroupId - id of the cgroup
+   * @return full path for the cgroup
+   */
+  public String getPathForCGroup(CGroupController controller, String
+      cGroupId);
+
+  /**
+   * Gets the full path for the cgroup's tasks file, given a controller and a
+   * cgroup id
+   * @param controller - controller type for the cgroup
+   * @param cGroupId - id of the cgroup
+   * @return full path for the cgroup's tasks file
+   */
+  public String getPathForCGroupTasks(CGroupController controller, String
+      cGroupId);
+
+  /**
+   * Gets the full path for a cgroup parameter, given a controller,
+   * cgroup id and parameter name
+   * @param controller - controller type for the cgroup
+   * @param cGroupId - id of the cgroup
+   * @param param - cgroup parameter ( e.g classid )
+   * @return full path for the cgroup parameter
+   */
+  public String getPathForCGroupParam(CGroupController controller, String
+      cGroupId, String param);
+
+  /**
+   * updates a cgroup parameter, given a controller, cgroup id, parameter name
+   * and a parameter value
+   * @param controller - controller type for the cgroup
+   * @param cGroupId - id of the cgroup
+   * @param param - cgroup parameter ( e.g classid )
+   * @param value - value to be written to the parameter file
+   * @throws ResourceHandlerException
+   */
+  public void updateCGroupParam(CGroupController controller, String cGroupId,
+      String param, String value) throws ResourceHandlerException;
+
+  /**
+   * reads a cgroup parameter value, given a controller, cgroup id, parameter
+   * name
+   * @param controller - controller type for the cgroup
+   * @param cGroupId - id of the cgroup
+   * @param param - cgroup parameter ( e.g classid )
+   * @return parameter value as read from the parameter file
+   * @throws ResourceHandlerException
+   */
+  public String getCGroupParam(CGroupController controller, String cGroupId,
+      String param) throws ResourceHandlerException;
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/838b06ac/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/CGroupsHandlerImpl.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/CGroupsHandlerImpl.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/CGroupsHandlerImpl.java
new file mode 100644
index 0000000..9a4230f
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/CGroupsHandlerImpl.java
@@ -0,0 +1,436 @@
+/*
+ * *
+ *  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.nodemanager.containermanager.linux.resources;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileUtil;
+import org.apache.hadoop.io.IOUtils;
+import org.apache.hadoop.yarn.conf.YarnConfiguration;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperation;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperationException;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperationExecutor;
+import org.apache.hadoop.yarn.util.Clock;
+import org.apache.hadoop.yarn.util.SystemClock;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Support for interacting with various CGroup subsystems. Thread-safe.
+ */
+
+@InterfaceAudience.Private
+@InterfaceStability.Unstable
+class CGroupsHandlerImpl implements CGroupsHandler {
+
+  private static final Log LOG = LogFactory.getLog(CGroupsHandlerImpl.class);
+  private static final String MTAB_FILE = "/proc/mounts";
+  private static final String CGROUPS_FSTYPE = "cgroup";
+
+  private final String cGroupPrefix;
+  private final boolean enableCGroupMount;
+  private final String cGroupMountPath;
+  private final long deleteCGroupTimeout;
+  private final long deleteCGroupDelay;
+  private final Map<CGroupController, String> controllerPaths;
+  private final ReadWriteLock rwLock;
+  private final PrivilegedOperationExecutor privilegedOperationExecutor;
+  private final Clock clock;
+
+  public CGroupsHandlerImpl(Configuration conf, PrivilegedOperationExecutor
+      privilegedOperationExecutor) throws ResourceHandlerException {
+    this.cGroupPrefix = conf.get(YarnConfiguration.
+        NM_LINUX_CONTAINER_CGROUPS_HIERARCHY, "/hadoop-yarn")
+        .replaceAll("^/", "").replaceAll("$/", "");
+    this.enableCGroupMount = conf.getBoolean(YarnConfiguration.
+        NM_LINUX_CONTAINER_CGROUPS_MOUNT, false);
+    this.cGroupMountPath = conf.get(YarnConfiguration.
+        NM_LINUX_CONTAINER_CGROUPS_MOUNT_PATH, null);
+    this.deleteCGroupTimeout = conf.getLong(
+        YarnConfiguration.NM_LINUX_CONTAINER_CGROUPS_DELETE_TIMEOUT,
+        YarnConfiguration.DEFAULT_NM_LINUX_CONTAINER_CGROUPS_DELETE_TIMEOUT);
+    this.deleteCGroupDelay =
+        conf.getLong(YarnConfiguration.NM_LINUX_CONTAINER_CGROUPS_DELETE_DELAY,
+            YarnConfiguration.DEFAULT_NM_LINUX_CONTAINER_CGROUPS_DELETE_DELAY);
+    this.controllerPaths = new HashMap<>();
+    this.rwLock = new ReentrantReadWriteLock();
+    this.privilegedOperationExecutor = privilegedOperationExecutor;
+    this.clock = new SystemClock();
+
+    init();
+  }
+
+  private void init() throws ResourceHandlerException {
+    initializeControllerPaths();
+  }
+
+  private String getControllerPath(CGroupController controller) {
+    try {
+      rwLock.readLock().lock();
+      return controllerPaths.get(controller);
+    } finally {
+      rwLock.readLock().unlock();
+    }
+  }
+
+  private void initializeControllerPaths() throws ResourceHandlerException {
+    if (enableCGroupMount) {
+      //nothing to do here - we support 'deferred' mounting of specific
+      //controllers - we'll populate the path for a given controller when an
+      //explicit mountCGroupController request is issued.
+      LOG.info("CGroup controller mounting enabled.");
+    } else {
+      //cluster admins are expected to have mounted controllers in specific
+      //locations - we'll attempt to figure out mount points
+      initializeControllerPathsFromMtab();
+    }
+  }
+
+  private void initializeControllerPathsFromMtab()
+      throws ResourceHandlerException {
+    try {
+      Map<String, List<String>> parsedMtab = parseMtab();
+
+      //we want to do a bulk update without the paths changing concurrently
+      rwLock.writeLock().lock();
+
+      for (CGroupController controller : CGroupController.values()) {
+        String name = controller.getName();
+        String controllerPath = findControllerInMtab(name, parsedMtab);
+
+        if (controllerPath != null) {
+          File f = new File(controllerPath + "/" + this.cGroupPrefix);
+
+          if (FileUtil.canWrite(f)) {
+            controllerPaths.put(controller, controllerPath);
+          } else {
+            String error =
+                new StringBuffer("Mount point Based on mtab file: ")
+                    .append(MTAB_FILE).append(
+                    ". Controller mount point not writable for: ")
+                    .append(name).toString();
+
+            LOG.error(error);
+            throw new ResourceHandlerException(error);
+          }
+        } else {
+
+            LOG.warn("Controller not mounted but automount disabled: " + name);
+        }
+      }
+    } catch (IOException e) {
+      LOG.warn("Failed to initialize controller paths! Exception: " + e);
+      throw new ResourceHandlerException(
+          "Failed to initialize controller paths!");
+    } finally {
+      rwLock.writeLock().unlock();
+    }
+  }
+
+  /* We are looking for entries of the form:
+   * none /cgroup/path/mem cgroup rw,memory 0 0
+   *
+   * Use a simple pattern that splits on the five spaces, and
+   * grabs the 2, 3, and 4th fields.
+   */
+
+  private static final Pattern MTAB_FILE_FORMAT = Pattern.compile(
+      "^[^\\s]+\\s([^\\s]+)\\s([^\\s]+)\\s([^\\s]+)\\s[^\\s]+\\s[^\\s]+$");
+
+  /*
+   * Returns a map: path -> mount options
+   * for mounts with type "cgroup". Cgroup controllers will
+   * appear in the list of options for a path.
+   */
+  private Map<String, List<String>> parseMtab() throws IOException {
+    Map<String, List<String>> ret = new HashMap<String, List<String>>();
+    BufferedReader in = null;
+
+    try {
+      FileInputStream fis = new FileInputStream(new File(getMtabFileName()));
+      in = new BufferedReader(new InputStreamReader(fis, "UTF-8"));
+
+      for (String str = in.readLine(); str != null;
+           str = in.readLine()) {
+        Matcher m = MTAB_FILE_FORMAT.matcher(str);
+        boolean mat = m.find();
+        if (mat) {
+          String path = m.group(1);
+          String type = m.group(2);
+          String options = m.group(3);
+
+          if (type.equals(CGROUPS_FSTYPE)) {
+            List<String> value = Arrays.asList(options.split(","));
+            ret.put(path, value);
+          }
+        }
+      }
+    } catch (IOException e) {
+      throw new IOException("Error while reading " + getMtabFileName(), e);
+    } finally {
+      IOUtils.cleanup(LOG, in);
+    }
+
+    return ret;
+  }
+
+  private String findControllerInMtab(String controller,
+      Map<String, List<String>> entries) {
+    for (Map.Entry<String, List<String>> e : entries.entrySet()) {
+      if (e.getValue().contains(controller))
+        return e.getKey();
+    }
+
+    return null;
+  }
+
+  String getMtabFileName() {
+    return MTAB_FILE;
+  }
+
+  @Override
+  public void mountCGroupController(CGroupController controller)
+      throws ResourceHandlerException {
+    if (!enableCGroupMount) {
+      LOG.warn("CGroup mounting is disabled - ignoring mount request for: " +
+          controller.getName());
+      return;
+    }
+
+    String path = getControllerPath(controller);
+
+    if (path == null) {
+      try {
+        //lock out other readers/writers till we are done
+        rwLock.writeLock().lock();
+
+        String hierarchy = cGroupPrefix;
+        StringBuffer controllerPath = new StringBuffer()
+            .append(cGroupMountPath).append('/').append(controller.getName());
+        StringBuffer cGroupKV = new StringBuffer()
+            .append(controller.getName()).append('=').append(controllerPath);
+        PrivilegedOperation.OperationType opType = PrivilegedOperation
+            .OperationType.MOUNT_CGROUPS;
+        PrivilegedOperation op = new PrivilegedOperation(opType, (String) null);
+
+        op.appendArgs(hierarchy, cGroupKV.toString());
+        LOG.info("Mounting controller " + controller.getName() + " at " +
+              controllerPath);
+        privilegedOperationExecutor.executePrivilegedOperation(op, false);
+
+        //if privileged operation succeeds, update controller paths
+        controllerPaths.put(controller, controllerPath.toString());
+
+        return;
+      } catch (PrivilegedOperationException e) {
+        LOG.error("Failed to mount controller: " + controller.getName());
+        throw new ResourceHandlerException("Failed to mount controller: "
+            + controller.getName());
+      } finally {
+        rwLock.writeLock().unlock();
+      }
+    } else {
+      LOG.info("CGroup controller already mounted at: " + path);
+      return;
+    }
+  }
+
+  @Override
+  public String getPathForCGroup(CGroupController controller, String cGroupId) {
+    return new StringBuffer(getControllerPath(controller))
+        .append('/').append(cGroupPrefix).append("/")
+        .append(cGroupId).toString();
+  }
+
+  @Override
+  public String getPathForCGroupTasks(CGroupController controller,
+      String cGroupId) {
+    return new StringBuffer(getPathForCGroup(controller, cGroupId))
+        .append('/').append(CGROUP_FILE_TASKS).toString();
+  }
+
+  @Override
+  public String getPathForCGroupParam(CGroupController controller,
+      String cGroupId, String param) {
+    return new StringBuffer(getPathForCGroup(controller, cGroupId))
+        .append('/').append(controller.getName()).append('.')
+        .append(param).toString();
+  }
+
+  @Override
+  public String createCGroup(CGroupController controller, String cGroupId)
+      throws ResourceHandlerException {
+    String path = getPathForCGroup(controller, cGroupId);
+
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("createCgroup: " + path);
+    }
+
+    if (!new File(path).mkdir()) {
+      throw new ResourceHandlerException("Failed to create cgroup at " + path);
+    }
+
+    return path;
+  }
+
+  /*
+  * Utility routine to print first line from cgroup tasks file
+  */
+  private void logLineFromTasksFile(File cgf) {
+    String str;
+    if (LOG.isDebugEnabled()) {
+      try (BufferedReader inl =
+          new BufferedReader(new InputStreamReader(new FileInputStream(cgf
+              + "/tasks"), "UTF-8"))) {
+        if ((str = inl.readLine()) != null) {
+          LOG.debug("First line in cgroup tasks file: " + cgf + " " + str);
+        }
+      } catch (IOException e) {
+        LOG.warn("Failed to read cgroup tasks file. ", e);
+      }
+    }
+  }
+
+  /**
+   * If tasks file is empty, delete the cgroup.
+   *
+   * @param cgf object referring to the cgroup to be deleted
+   * @return Boolean indicating whether cgroup was deleted
+   */
+  boolean checkAndDeleteCgroup(File cgf) throws InterruptedException {
+    boolean deleted = false;
+    // FileInputStream in = null;
+    try (FileInputStream in = new FileInputStream(cgf + "/tasks")) {
+      if (in.read() == -1) {
+        /*
+         * "tasks" file is empty, sleep a bit more and then try to delete the
+         * cgroup. Some versions of linux will occasionally panic due to a race
+         * condition in this area, hence the paranoia.
+         */
+        Thread.sleep(deleteCGroupDelay);
+        deleted = cgf.delete();
+        if (!deleted) {
+          LOG.warn("Failed attempt to delete cgroup: " + cgf);
+        }
+      } else {
+        logLineFromTasksFile(cgf);
+      }
+    } catch (IOException e) {
+      LOG.warn("Failed to read cgroup tasks file. ", e);
+    }
+    return deleted;
+  }
+
+  @Override
+  public void deleteCGroup(CGroupController controller, String cGroupId)
+      throws ResourceHandlerException {
+    boolean deleted = false;
+    String cGroupPath = getPathForCGroup(controller, cGroupId);
+
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("deleteCGroup: " + cGroupPath);
+    }
+
+    long start = clock.getTime();
+
+    do {
+      try {
+        deleted = checkAndDeleteCgroup(new File(cGroupPath));
+        if (!deleted) {
+          Thread.sleep(deleteCGroupDelay);
+        }
+      } catch (InterruptedException ex) {
+        // NOP
+      }
+    } while (!deleted && (clock.getTime() - start) < deleteCGroupTimeout);
+
+    if (!deleted) {
+      LOG.warn("Unable to delete  " + cGroupPath +
+          ", tried to delete for " + deleteCGroupTimeout + "ms");
+    }
+  }
+
+  @Override
+  public void updateCGroupParam(CGroupController controller, String cGroupId,
+      String param, String value) throws ResourceHandlerException {
+    String cGroupParamPath = getPathForCGroupParam(controller, cGroupId, param);
+    PrintWriter pw = null;
+
+    if (LOG.isDebugEnabled()) {
+      LOG.debug(
+          "updateCGroupParam for path: " + cGroupParamPath + " with value " +
+              value);
+    }
+
+    try {
+      File file = new File(cGroupParamPath);
+      Writer w = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
+      pw = new PrintWriter(w);
+      pw.write(value);
+    } catch (IOException e) {
+      throw new ResourceHandlerException(new StringBuffer("Unable to write to ")
+          .append(cGroupParamPath).append(" with value: ").append(value)
+          .toString(), e);
+    } finally {
+      if (pw != null) {
+        boolean hasError = pw.checkError();
+        pw.close();
+        if (hasError) {
+          throw new ResourceHandlerException(
+              new StringBuffer("Unable to write to ")
+                  .append(cGroupParamPath).append(" with value: ").append(value)
+                  .toString());
+        }
+        if (pw.checkError()) {
+          throw new ResourceHandlerException("Error while closing cgroup file" +
+              " " + cGroupParamPath);
+        }
+      }
+    }
+  }
+
+  @Override
+  public String getCGroupParam(CGroupController controller, String cGroupId,
+      String param) throws ResourceHandlerException {
+    String cGroupParamPath = getPathForCGroupParam(controller, cGroupId, param);
+
+    try {
+      byte[] contents = Files.readAllBytes(Paths.get(cGroupParamPath));
+      return new String(contents, "UTF-8").trim();
+    } catch (IOException e) {
+      throw new ResourceHandlerException(
+          "Unable to read from " + cGroupParamPath);
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/hadoop/blob/838b06ac/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/ResourceHandler.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/ResourceHandler.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/ResourceHandler.java
new file mode 100644
index 0000000..3dfc86b
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/ResourceHandler.java
@@ -0,0 +1,91 @@
+/*
+ * *
+ *  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.nodemanager.containermanager.linux.resources;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.yarn.api.records.ContainerId;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.Container;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperation;
+
+import java.util.List;
+
+/**
+ * Handler interface for resource subsystems' isolation and enforcement. e.g cpu, memory, network, disks etc
+ */
+
+@InterfaceAudience.Private
+@InterfaceStability.Unstable
+public interface ResourceHandler {
+
+  /**
+   * Bootstrap resource susbsystem.
+   *
+   * @return (possibly empty) list of operations that require elevated
+   * privileges
+   */
+  List<PrivilegedOperation> bootstrap(Configuration configuration)
+      throws ResourceHandlerException;
+
+  /**
+   * Prepare a resource environment for container launch
+   *
+   * @param container Container being launched
+   * @return (possibly empty) list of operations that require elevated
+   * privileges e.g a) create a custom cgroup b) add pid for container to tasks
+   * file for a cgroup.
+   * @throws ResourceHandlerException
+   */
+  List<PrivilegedOperation> preStart(Container container)
+      throws ResourceHandlerException;
+
+  /**
+   * Require state for container that was already launched
+   *
+   * @param containerId if of the container being reacquired.
+   * @return (possibly empty) list of operations that require elevated
+   * privileges
+   * @throws ResourceHandlerException
+   */
+
+  List<PrivilegedOperation> reacquireContainer(ContainerId containerId)
+      throws ResourceHandlerException;
+
+  /**
+   * Perform any tasks necessary after container completion
+   * @param containerId of the container that was completed.
+   * @return (possibly empty) list of operations that require elevated
+   * privileges
+   * @throws ResourceHandlerException
+   */
+  List<PrivilegedOperation> postComplete(ContainerId containerId) throws
+      ResourceHandlerException;
+
+  /**
+   * Teardown environment for resource subsystem if requested. This method
+   * needs to be used with care since it could impact running containers.
+   *
+   * @return (possibly empty) list of operations that require elevated
+   * privileges
+   */
+  List<PrivilegedOperation> teardown() throws ResourceHandlerException;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/hadoop/blob/838b06ac/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/ResourceHandlerChain.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/ResourceHandlerChain.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/ResourceHandlerChain.java
new file mode 100644
index 0000000..955d216
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/ResourceHandlerChain.java
@@ -0,0 +1,142 @@
+/*
+ * *
+ *  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.nodemanager.containermanager.linux.resources;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.yarn.api.records.ContainerId;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.Container;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperation;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A helper class to delegate funcationality to a 'chain' of
+ * ResourceHandler(s)
+ */
+
+@InterfaceAudience.Private
+@InterfaceStability.Unstable
+public class ResourceHandlerChain implements ResourceHandler {
+  private final List<ResourceHandler> resourceHandlers;
+
+  public ResourceHandlerChain(List<ResourceHandler> resourceHandlers) {
+    this.resourceHandlers = resourceHandlers;
+  }
+
+  @Override
+  public List<PrivilegedOperation> bootstrap(Configuration configuration)
+      throws ResourceHandlerException {
+
+    List<PrivilegedOperation> allOperations = new
+        ArrayList<PrivilegedOperation>();
+
+    for (ResourceHandler resourceHandler : resourceHandlers) {
+      List<PrivilegedOperation> handlerOperations =
+          resourceHandler.bootstrap(configuration);
+      if (handlerOperations != null) {
+        allOperations.addAll(handlerOperations);
+      }
+
+    }
+    return allOperations;
+  }
+
+  @Override
+  public List<PrivilegedOperation> preStart(Container container)
+      throws ResourceHandlerException {
+    List<PrivilegedOperation> allOperations = new
+        ArrayList<PrivilegedOperation>();
+
+    for (ResourceHandler resourceHandler : resourceHandlers) {
+      List<PrivilegedOperation> handlerOperations =
+          resourceHandler.preStart(container);
+
+      if (handlerOperations != null) {
+        allOperations.addAll(handlerOperations);
+      }
+
+    }
+    return allOperations;
+  }
+
+  @Override
+  public List<PrivilegedOperation> reacquireContainer(ContainerId containerId)
+      throws ResourceHandlerException {
+    List<PrivilegedOperation> allOperations = new
+        ArrayList<PrivilegedOperation>();
+
+    for (ResourceHandler resourceHandler : resourceHandlers) {
+      List<PrivilegedOperation> handlerOperations =
+          resourceHandler.reacquireContainer(containerId);
+
+      if (handlerOperations != null) {
+        allOperations.addAll(handlerOperations);
+      }
+
+    }
+    return allOperations;
+  }
+
+  @Override
+  public List<PrivilegedOperation> postComplete(ContainerId containerId)
+      throws ResourceHandlerException {
+    List<PrivilegedOperation> allOperations = new
+        ArrayList<PrivilegedOperation>();
+
+    for (ResourceHandler resourceHandler : resourceHandlers) {
+      List<PrivilegedOperation> handlerOperations =
+          resourceHandler.postComplete(containerId);
+
+      if (handlerOperations != null) {
+        allOperations.addAll(handlerOperations);
+      }
+
+    }
+    return allOperations;
+  }
+
+  @Override
+  public List<PrivilegedOperation> teardown()
+      throws ResourceHandlerException {
+    List<PrivilegedOperation> allOperations = new
+        ArrayList<PrivilegedOperation>();
+
+    for (ResourceHandler resourceHandler : resourceHandlers) {
+      List<PrivilegedOperation> handlerOperations =
+          resourceHandler.teardown();
+
+      if (handlerOperations != null) {
+        allOperations.addAll(handlerOperations);
+      }
+
+    }
+    return allOperations;
+  }
+
+  List<ResourceHandler> getResourceHandlerList() {
+    return Collections.unmodifiableList(resourceHandlers);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/838b06ac/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/ResourceHandlerException.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/ResourceHandlerException.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/ResourceHandlerException.java
new file mode 100644
index 0000000..3ab7548
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/ResourceHandlerException.java
@@ -0,0 +1,47 @@
+/*
+ * *
+ *  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.nodemanager.containermanager.linux.resources;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.yarn.exceptions.YarnException;
+
+@InterfaceAudience.Private
+@InterfaceStability.Unstable
+public class ResourceHandlerException extends YarnException {
+  private static final long serialVersionUID = 1L;
+
+  public ResourceHandlerException() {
+    super();
+  }
+
+  public ResourceHandlerException(String message) {
+    super(message);
+  }
+
+  public ResourceHandlerException(Throwable cause) {
+    super(cause);
+  }
+
+  public ResourceHandlerException(String message, Throwable cause) {
+    super(message, cause);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/hadoop/blob/838b06ac/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/privileged/TestPrivilegedOperationExecutor.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/privileged/TestPrivilegedOperationExecutor.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/privileged/TestPrivilegedOperationExecutor.java
new file mode 100644
index 0000000..7154d03
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/privileged/TestPrivilegedOperationExecutor.java
@@ -0,0 +1,233 @@
+/*
+ * *
+ *  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.nodemanager.containermanager.linux.privileged;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.yarn.conf.YarnConfiguration;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class TestPrivilegedOperationExecutor {
+  private static final Log LOG = LogFactory
+      .getLog(TestPrivilegedOperationExecutor.class);
+  private String localDataDir;
+  private String customExecutorPath;
+  private Configuration nullConf = null;
+  private Configuration emptyConf;
+  private Configuration confWithExecutorPath;
+
+  private String cGroupTasksNone;
+  private String cGroupTasksInvalid;
+  private String cGroupTasks1;
+  private String cGroupTasks2;
+  private String cGroupTasks3;
+  private PrivilegedOperation opDisallowed;
+  private PrivilegedOperation opTasksNone;
+  private PrivilegedOperation opTasksInvalid;
+  private PrivilegedOperation opTasks1;
+  private PrivilegedOperation opTasks2;
+  private PrivilegedOperation opTasks3;
+
+  @Before
+  public void setup() {
+    localDataDir = System.getProperty("test.build.data");
+    customExecutorPath = localDataDir + "/bin/container-executor";
+    emptyConf = new YarnConfiguration();
+    confWithExecutorPath = new YarnConfiguration();
+    confWithExecutorPath.set(YarnConfiguration
+        .NM_LINUX_CONTAINER_EXECUTOR_PATH, customExecutorPath);
+
+    cGroupTasksNone = "none";
+    cGroupTasksInvalid = "invalid_string";
+    cGroupTasks1 = "cpu/hadoop_yarn/container_01/tasks";
+    cGroupTasks2 = "net_cls/hadoop_yarn/container_01/tasks";
+    cGroupTasks3 = "blkio/hadoop_yarn/container_01/tasks";
+    opDisallowed = new PrivilegedOperation
+        (PrivilegedOperation.OperationType.DELETE_AS_USER, (String) null);
+    opTasksNone = new PrivilegedOperation
+        (PrivilegedOperation.OperationType.ADD_PID_TO_CGROUP,
+            PrivilegedOperation.CGROUP_ARG_PREFIX + cGroupTasksNone);
+    opTasksInvalid = new PrivilegedOperation
+        (PrivilegedOperation.OperationType.ADD_PID_TO_CGROUP,
+            cGroupTasksInvalid);
+    opTasks1 = new PrivilegedOperation
+        (PrivilegedOperation.OperationType.ADD_PID_TO_CGROUP,
+            PrivilegedOperation.CGROUP_ARG_PREFIX + cGroupTasks1);
+    opTasks2 = new PrivilegedOperation
+        (PrivilegedOperation.OperationType.ADD_PID_TO_CGROUP,
+            PrivilegedOperation.CGROUP_ARG_PREFIX + cGroupTasks2);
+    opTasks3 = new PrivilegedOperation
+        (PrivilegedOperation.OperationType.ADD_PID_TO_CGROUP,
+            PrivilegedOperation.CGROUP_ARG_PREFIX + cGroupTasks3);
+  }
+
+  @Test
+  public void testExecutorPath() {
+    String containerExePath = PrivilegedOperationExecutor
+        .getContainerExecutorExecutablePath(nullConf);
+
+    //In case HADOOP_YARN_HOME isn't set, CWD is used. If conf is null or
+    //NM_LINUX_CONTAINER_EXECUTOR_PATH is not set, then a defaultPath is
+    //constructed.
+    String yarnHomeEnvVar = System.getenv("HADOOP_YARN_HOME");
+    String yarnHome = yarnHomeEnvVar != null ? yarnHomeEnvVar
+        : new File("").getAbsolutePath();
+    String expectedPath = yarnHome + "/bin/container-executor";
+
+    Assert.assertEquals(expectedPath, containerExePath);
+
+    containerExePath = PrivilegedOperationExecutor
+        .getContainerExecutorExecutablePath(emptyConf);
+    Assert.assertEquals(expectedPath, containerExePath);
+
+    //if NM_LINUX_CONTAINER_EXECUTOR_PATH is set, this must be returned
+    expectedPath = customExecutorPath;
+    containerExePath = PrivilegedOperationExecutor
+        .getContainerExecutorExecutablePath(confWithExecutorPath);
+    Assert.assertEquals(expectedPath, containerExePath);
+  }
+
+  @Test
+  public void testExecutionCommand() {
+    PrivilegedOperationExecutor exec = PrivilegedOperationExecutor
+        .getInstance(confWithExecutorPath);
+    PrivilegedOperation op = new PrivilegedOperation(PrivilegedOperation
+        .OperationType.LAUNCH_CONTAINER, (String) null);
+    String[] cmdArray = exec.getPrivilegedOperationExecutionCommand(null, op);
+
+    //No arguments added - so the resulting array should consist of
+    //1)full path to executor 2) cli switch
+    Assert.assertEquals(2, cmdArray.length);
+    Assert.assertEquals(customExecutorPath, cmdArray[0]);
+    Assert.assertEquals(op.getOperationType().getOption(), cmdArray[1]);
+
+    //other (dummy) arguments to launch container
+    String[] additionalArgs = { "test_user", "yarn", "1", "app_01",
+        "container_01", "workdir", "launch_script.sh", "tokens", "pidfile",
+        "nm-local-dirs", "nm-log-dirs", "resource-spec" };
+
+    op.appendArgs(additionalArgs);
+    cmdArray = exec.getPrivilegedOperationExecutionCommand(null, op);
+
+    //Resulting array should be of length 2 greater than the number of
+    //additional arguments added.
+
+    Assert.assertEquals(2 + additionalArgs.length, cmdArray.length);
+    Assert.assertEquals(customExecutorPath, cmdArray[0]);
+    Assert.assertEquals(op.getOperationType().getOption(), cmdArray[1]);
+
+    //Rest of args should be same as additional args.
+    for (int i = 0; i < additionalArgs.length; ++i) {
+      Assert.assertEquals(additionalArgs[i], cmdArray[2 + i]);
+    }
+
+    //Now test prefix commands
+    List<String> prefixCommands = Arrays.asList("nice", "-10");
+    cmdArray = exec.getPrivilegedOperationExecutionCommand(prefixCommands, op);
+    int prefixLength = prefixCommands.size();
+    //Resulting array should be of length of prefix command args + 2 (exec
+    // path + switch) + length of additional args.
+    Assert.assertEquals(prefixLength + 2 + additionalArgs.length,
+        cmdArray.length);
+
+    //Prefix command array comes first
+    for (int i = 0; i < prefixLength; ++i) {
+      Assert.assertEquals(prefixCommands.get(i), cmdArray[i]);
+    }
+
+    //Followed by the container executor path and the cli switch
+    Assert.assertEquals(customExecutorPath, cmdArray[prefixLength]);
+    Assert.assertEquals(op.getOperationType().getOption(),
+        cmdArray[prefixLength + 1]);
+
+    //Followed by the rest of the args
+    //Rest of args should be same as additional args.
+    for (int i = 0; i < additionalArgs.length; ++i) {
+      Assert.assertEquals(additionalArgs[i], cmdArray[prefixLength + 2 + i]);
+    }
+  }
+
+  @Test
+  public void testSquashCGroupOperationsWithInvalidOperations() {
+    List<PrivilegedOperation> ops = new ArrayList<>();
+
+    //Ensure that disallowed ops are rejected
+    ops.add(opTasksNone);
+    ops.add(opDisallowed);
+
+    try {
+      PrivilegedOperationExecutor.squashCGroupOperations(ops);
+      Assert.fail("Expected squash operation to fail with an exception!");
+    } catch (PrivilegedOperationException e) {
+      LOG.info("Caught expected exception : " + e);
+    }
+
+    //Ensure that invalid strings are rejected
+    ops.clear();
+    ops.add(opTasksNone);
+    ops.add(opTasksInvalid);
+
+    try {
+      PrivilegedOperationExecutor.squashCGroupOperations(ops);
+      Assert.fail("Expected squash operation to fail with an exception!");
+    } catch (PrivilegedOperationException e) {
+      LOG.info("Caught expected exception : " + e);
+    }
+  }
+
+  @Test
+  public void testSquashCGroupOperationsWithValidOperations() {
+    List<PrivilegedOperation> ops = new ArrayList<>();
+    //Test squashing, including 'none'
+    ops.clear();
+    ops.add(opTasks1);
+    //this is expected to be ignored
+    ops.add(opTasksNone);
+    ops.add(opTasks2);
+    ops.add(opTasks3);
+
+    try {
+      PrivilegedOperation op = PrivilegedOperationExecutor
+          .squashCGroupOperations(ops);
+      String expected = new StringBuffer
+          (PrivilegedOperation.CGROUP_ARG_PREFIX)
+          .append(cGroupTasks1).append(',')
+          .append(cGroupTasks2).append(',')
+          .append(cGroupTasks3).toString();
+
+      //We expect axactly one argument
+      Assert.assertEquals(1, op.getArguments().size());
+      //Squashed list of tasks files
+      Assert.assertEquals(expected, op.getArguments().get(0));
+    } catch (PrivilegedOperationException e) {
+      LOG.info("Caught unexpected exception : " + e);
+      Assert.fail("Caught unexpected exception: " + e);
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/hadoop/blob/838b06ac/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/TestCGroupsHandlerImpl.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/TestCGroupsHandlerImpl.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/TestCGroupsHandlerImpl.java
new file mode 100644
index 0000000..0717447
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/TestCGroupsHandlerImpl.java
@@ -0,0 +1,235 @@
+/*
+ * *
+ *  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.nodemanager.containermanager.linux.resources;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileUtil;
+import org.apache.hadoop.yarn.conf.YarnConfiguration;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperation;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperationException;
+import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.privileged.PrivilegedOperationExecutor;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.List;
+import java.util.Map;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+public class TestCGroupsHandlerImpl {
+  private static final Log LOG =
+      LogFactory.getLog(TestCGroupsHandlerImpl.class);
+
+  private PrivilegedOperationExecutor privilegedOperationExecutorMock;
+  private Configuration conf;
+  private String tmpPath;
+  private String hierarchy;
+  private CGroupsHandler.CGroupController controller;
+  private String controllerPath;
+
+  @Before
+  public void setup() {
+    privilegedOperationExecutorMock = mock(PrivilegedOperationExecutor.class);
+    conf = new YarnConfiguration();
+    tmpPath = System.getProperty("test.build.data") + "/cgroups";
+    //no leading or trailing slashes here
+    hierarchy = "test-hadoop-yarn";
+
+    conf.set(YarnConfiguration.NM_LINUX_CONTAINER_CGROUPS_HIERARCHY, hierarchy);
+    conf.setBoolean(YarnConfiguration.NM_LINUX_CONTAINER_CGROUPS_MOUNT, true);
+    conf.set(YarnConfiguration.NM_LINUX_CONTAINER_CGROUPS_MOUNT_PATH, tmpPath);
+    controller = CGroupsHandler.CGroupController.NET_CLS;
+    controllerPath = new StringBuffer(tmpPath).append('/')
+        .append(controller.getName()).append('/').append(hierarchy).toString();
+  }
+
+  @Test
+  public void testMountController() {
+    CGroupsHandler cGroupsHandler = null;
+    //Since we enabled (deferred) cgroup controller mounting, no interactions
+    //should have occurred, with this mock
+    verifyZeroInteractions(privilegedOperationExecutorMock);
+
+    try {
+      cGroupsHandler = new CGroupsHandlerImpl(conf,
+          privilegedOperationExecutorMock);
+      PrivilegedOperation expectedOp = new PrivilegedOperation
+          (PrivilegedOperation.OperationType.MOUNT_CGROUPS, (String) null);
+      //This is expected to be of the form :
+      //net_cls=<mount_path>/net_cls
+      StringBuffer controllerKV = new StringBuffer(controller.getName())
+          .append('=').append(tmpPath).append('/').append(controller.getName());
+      expectedOp.appendArgs(hierarchy, controllerKV.toString());
+
+      cGroupsHandler.mountCGroupController(controller);
+      try {
+        ArgumentCaptor<PrivilegedOperation> opCaptor = ArgumentCaptor.forClass
+            (PrivilegedOperation.class);
+        verify(privilegedOperationExecutorMock)
+            .executePrivilegedOperation(opCaptor.capture(), eq(false));
+
+        //we'll explicitly capture and assert that the
+        //captured op and the expected op are identical.
+        Assert.assertEquals(expectedOp, opCaptor.getValue());
+        verifyNoMoreInteractions(privilegedOperationExecutorMock);
+
+        //Try mounting the same controller again - this should be a no-op
+        cGroupsHandler.mountCGroupController(controller);
+        verifyNoMoreInteractions(privilegedOperationExecutorMock);
+      } catch (PrivilegedOperationException e) {
+        LOG.error("Caught exception: " + e);
+        Assert.assertTrue("Unexpected PrivilegedOperationException from mock!",
+            false);
+      }
+    } catch (ResourceHandlerException e) {
+      LOG.error("Caught exception: " + e);
+      Assert.assertTrue("Unexpected ResourceHandler Exception!", false);
+    }
+  }
+
+  @Test
+  public void testCGroupPaths() {
+    //As per junit behavior, we expect a new mock object to be available
+    //in this test.
+    verifyZeroInteractions(privilegedOperationExecutorMock);
+    CGroupsHandler cGroupsHandler = null;
+    try {
+      cGroupsHandler = new CGroupsHandlerImpl(conf,
+          privilegedOperationExecutorMock);
+      cGroupsHandler.mountCGroupController(controller);
+    } catch (ResourceHandlerException e) {
+      LOG.error("Caught exception: " + e);
+      Assert.assertTrue(
+          "Unexpected ResourceHandlerException when mounting controller!",
+          false);
+    }
+
+    String testCGroup = "container_01";
+    String expectedPath = new StringBuffer(controllerPath).append('/')
+        .append(testCGroup).toString();
+    String path = cGroupsHandler.getPathForCGroup(controller, testCGroup);
+    Assert.assertEquals(expectedPath, path);
+
+    String expectedPathTasks = new StringBuffer(expectedPath).append('/')
+        .append(CGroupsHandler.CGROUP_FILE_TASKS).toString();
+    path = cGroupsHandler.getPathForCGroupTasks(controller, testCGroup);
+    Assert.assertEquals(expectedPathTasks, path);
+
+    String param = CGroupsHandler.CGROUP_PARAM_CLASSID;
+    String expectedPathParam = new StringBuffer(expectedPath).append('/')
+        .append(controller.getName()).append('.').append(param).toString();
+    path = cGroupsHandler.getPathForCGroupParam(controller, testCGroup, param);
+    Assert.assertEquals(expectedPathParam, path);
+  }
+
+  @Test
+  public void testCGroupOperations() {
+    //As per junit behavior, we expect a new mock object to be available
+    //in this test.
+    verifyZeroInteractions(privilegedOperationExecutorMock);
+    CGroupsHandler cGroupsHandler = null;
+
+    try {
+      cGroupsHandler = new CGroupsHandlerImpl(conf,
+          privilegedOperationExecutorMock);
+      cGroupsHandler.mountCGroupController(controller);
+    } catch (ResourceHandlerException e) {
+      LOG.error("Caught exception: " + e);
+      Assert.assertTrue(
+          "Unexpected ResourceHandlerException when mounting controller!",
+          false);
+    }
+    //Lets manually create a path to (partially) simulate a mounted controller
+    //this is required because the handler uses a mocked privileged operation
+    //executor
+    new File(controllerPath).mkdirs();
+
+    String testCGroup = "container_01";
+    String expectedPath = new StringBuffer(controllerPath).append('/')
+        .append(testCGroup).toString();
+    try {
+      String path = cGroupsHandler.createCGroup(controller, testCGroup);
+
+      Assert.assertTrue(new File(expectedPath).exists());
+      Assert.assertEquals(expectedPath, path);
+
+      //update param and read param tests.
+      //We don't use net_cls.classid because as a test param here because
+      //cgroups provides very specific read/write semantics for classid (only
+      //numbers can be written - potentially as hex but can be read out only
+      //as decimal)
+      String param = "test_param";
+      String paramValue = "test_param_value";
+
+      cGroupsHandler
+          .updateCGroupParam(controller, testCGroup, param, paramValue);
+      String paramPath = new StringBuffer(expectedPath).append('/')
+          .append(controller.getName()).append('.').append(param).toString();
+      File paramFile = new File(paramPath);
+
+      Assert.assertTrue(paramFile.exists());
+      try {
+        Assert.assertEquals(paramValue, new String(Files.readAllBytes
+            (paramFile
+                .toPath())));
+      } catch (IOException e) {
+        LOG.error("Caught exception: " + e);
+        Assert.assertTrue("Unexpected IOException trying to read cgroup param!",
+            false);
+      }
+
+      Assert.assertEquals(paramValue, cGroupsHandler.getCGroupParam
+          (controller, testCGroup, param));
+
+      //We can't really do a delete test here. Linux cgroups
+      //implementation provides additional semantics - the cgroup cannot be
+      //deleted if there are any tasks still running in the cgroup even if
+      //the user attempting the delete has the file permissions to do so - we
+      //cannot simulate that here. Even if we create a dummy 'tasks' file, we
+      //wouldn't be able to simulate the delete behavior we need, since a cgroup
+      //can be deleted using using 'rmdir' if the tasks file is empty. Such a
+      //delete is not possible with a regular non-empty directory.
+    } catch (ResourceHandlerException e) {
+      LOG.error("Caught exception: " + e);
+      Assert.assertTrue(
+          "Unexpected ResourceHandlerException during cgroup operations!",
+          false);
+    }
+  }
+
+  @After
+  public void teardown() {
+    FileUtil.fullyDelete(new File(tmpPath));
+  }
+}
\ No newline at end of file