From 78a3a5c06e03a74dd0e4150c9017560c188b0c6f Mon Sep 17 00:00:00 2001 From: Miklos Szegedi Date: Fri, 2 Jun 2017 14:55:26 -0700 Subject: [PATCH 1/3] YARN-6668 Add new CGroupsResourceCalculator --- .../yarn/util/ResourceCalculatorProcessTree.java | 2 +- .../linux/resources/CGroupsHandler.java | 9 +- .../linux/resources/CGroupsHandlerImpl.java | 2 +- .../linux/resources/CGroupsResourceCalculator.java | 292 +++++++++++++++++++++ .../monitor/ContainersMonitorImpl.java | 32 ++- .../resources/TestCGroupsResourceCalculator.java | 167 ++++++++++++ 6 files changed, 498 insertions(+), 6 deletions(-) create mode 100644 hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/CGroupsResourceCalculator.java create mode 100644 hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/TestCGroupsResourceCalculator.java diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/ResourceCalculatorProcessTree.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/ResourceCalculatorProcessTree.java index 7e5cf551648..adbba84767b 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/ResourceCalculatorProcessTree.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/ResourceCalculatorProcessTree.java @@ -37,7 +37,7 @@ @Public @Evolving public abstract class ResourceCalculatorProcessTree extends Configured { - static final Log LOG = LogFactory + static protected final Log LOG = LogFactory .getLog(ResourceCalculatorProcessTree.class); public static final int UNAVAILABLE = -1; 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 index 8fc35a8232d..f093c2b9813 100644 --- 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 @@ -51,7 +51,7 @@ this.name = name; } - String getName() { + public String getName() { return name; } } @@ -98,6 +98,13 @@ void deleteCGroup(CGroupController controller, String cGroupId) throws ResourceHandlerException; /** + * Gets the absolute path to the specified cgroup controller. + * @param controller - controller type for the cgroup + * @return the root of the controller. + */ + String getControllerPath(CGroupController controller); + + /** * Gets the relative path for the cgroup, independent of a controller, for a * given cgroup id. * @param cGroupId - id of the cgroup 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 index 85b01cd1e83..f2c85dfbe5d 100644 --- 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 @@ -124,7 +124,7 @@ private void init() throws ResourceHandlerException { initializeControllerPaths(); } - private String getControllerPath(CGroupController controller) { + public String getControllerPath(CGroupController controller) { try { rwLock.readLock().lock(); return controllerPaths.get(controller); 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/CGroupsResourceCalculator.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/CGroupsResourceCalculator.java new file mode 100644 index 00000000000..466f7676e08 --- /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/CGroupsResourceCalculator.java @@ -0,0 +1,292 @@ +/** + * 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.util.CpuTimeTracker; +import org.apache.hadoop.util.Shell; +import org.apache.hadoop.util.SysInfoLinux; +import org.apache.hadoop.yarn.exceptions.YarnException; +import org.apache.hadoop.yarn.util.Clock; +import org.apache.hadoop.yarn.util.ResourceCalculatorProcessTree; +import org.apache.hadoop.yarn.util.SystemClock; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.math.BigInteger; +import java.nio.charset.Charset; +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A cgroups file-system based Resource calculator without the process tree + * features. + */ + +public class CGroupsResourceCalculator extends ResourceCalculatorProcessTree { + enum Result { + Continue, + Exit + } + private static final String PROCFS = "/proc"; + static final String CGROUP = "cgroup"; + static final String CPU_STAT = "cpuacct.stat"; + static final String MEM_STAT = "memory.usage_in_bytes"; + static final String MEMSW_STAT = "memory.memsw.usage_in_bytes"; + private static final String USER = "user "; + private static final String SYSTEM = "system "; + + private static final Pattern CGROUP_FILE_FORMAT = Pattern.compile( + "^(\\d+):([^:]+):/(.*)$"); + private final String procfsDir; + private CGroupsHandler cGroupsHandler; + + private String pid; + private File cpuStat; + private File memStat; + private File memswStat; + + private final long jiffyLengthMs; + private BigInteger processTotalJiffies = BigInteger.ZERO; + private final CpuTimeTracker cpuTimeTracker; + private Clock clock; + + private long mem = UNAVAILABLE; + private long memSw = UNAVAILABLE; + + public CGroupsResourceCalculator(String pid) throws YarnException { + this(pid, PROCFS, ResourceHandlerModule.getCGroupsHandler(), + SystemClock.getInstance()); + } + + CGroupsResourceCalculator(String pid, String procfsDir, + CGroupsHandler cGroupsHandler, Clock clock) + throws YarnException { + super(pid); + this.procfsDir = procfsDir; + this.cGroupsHandler = cGroupsHandler; + this.pid = pid; + this.cpuTimeTracker = + new CpuTimeTracker(SysInfoLinux.JIFFY_LENGTH_IN_MILLIS); + this.clock = clock; + this.jiffyLengthMs = (clock == SystemClock.getInstance()) ? + SysInfoLinux.JIFFY_LENGTH_IN_MILLIS : 10; + setCGroupFilePaths(); + } + + @Override + public float getCpuUsagePercent() { + readTotalProcessJiffies(); + cpuTimeTracker.updateElapsedJiffies( + processTotalJiffies, + clock.getTime()); + return cpuTimeTracker.getCpuTrackerUsagePercent(); + } + + @Override + public long getCumulativeCpuTime() { + if (jiffyLengthMs < 0) { + return UNAVAILABLE; + } + readTotalProcessJiffies(); + return + processTotalJiffies.longValue() * jiffyLengthMs; + } + + @Override + public long getRssMemorySize(int olderThanAge) { + if (olderThanAge > 0) { + return 0; + } + try { + processFile(memStat, (String line) -> { + mem = Long.parseLong(line); + return Result.Exit; + }); + return mem; + } catch (YarnException e) { + LOG.warn("Failed to parse cgroups " + memswStat, e); + } + return UNAVAILABLE; + } + + @Override + public long getVirtualMemorySize(int olderThanAge) { + if (olderThanAge > 0) { + return 0; + } + try { + processFile(memswStat, (String line) -> { + memSw = Long.parseLong(line); + return Result.Exit; + }); + return memSw; + } catch (YarnException e) { + LOG.warn("Failed to parse cgroups " + memswStat, e); + } + return UNAVAILABLE; + } + + @Override + public void updateProcessTree() { + } + + @Override + public String getProcessTreeDump() { + // We do not have a process tree in cgroups return just the pid for tracking + return pid; + } + + @Override + public boolean checkPidPgrpidForMatch() { + // We do not have a process tree in cgroups returning default ok + return true; + } + + /** + * Checks if the CGroupsResourceCalculator is available on this system. + * + * @return true if CGroupsResourceCalculator is available. False otherwise. + */ + public static boolean isAvailable() { + try { + if (!Shell.LINUX) { + LOG.info("CGroupsResourceCalculator currently is supported only on " + + "Linux."); + return false; + } + if (ResourceHandlerModule.getCGroupsHandler() == null) { + LOG.info("CGroupsResourceCalculator requires enabling CGroups"); + return false; + } + } catch (SecurityException se) { + LOG.warn("Failed to get Operating System name. " + se); + return false; + } + return true; + } + + private void readTotalProcessJiffies() { + try { + final BigInteger[] totalCPUTimeJiffies = new BigInteger[1]; + totalCPUTimeJiffies[0] = BigInteger.ZERO; + processFile(cpuStat, (String line) -> { + if (line.startsWith(USER)) { + totalCPUTimeJiffies[0] = totalCPUTimeJiffies[0].add( + new BigInteger(line.substring(USER.length()))); + } + if (line.startsWith(SYSTEM)) { + totalCPUTimeJiffies[0] = totalCPUTimeJiffies[0].add( + new BigInteger(line.substring(SYSTEM.length()))); + } + return Result.Continue; + }); + processTotalJiffies = totalCPUTimeJiffies[0]; + } catch (YarnException e) { + LOG.warn("Failed to parse " + pid, e); + } + } + + private String getCGroupRelativePathForPid( + CGroupsHandler.CGroupController controller) + throws YarnException { + File pidCgroupFile = new File(new File(procfsDir, pid), CGROUP); + String[] result = new String[1]; + processFile(pidCgroupFile, (String line)->{ + Matcher m = CGROUP_FILE_FORMAT.matcher(line); + boolean mat = m.find(); + if (mat) { + if (m.group(2).contains(controller.getName())) { + result[0] = m.group(3); + return Result.Exit; + } + } else { + LOG.warn( + "Unexpected: cgroup file is not in the expected format" + + " for process with pid " + pid); + } + return Result.Continue; + }); + if (result[0] == null) { + throw new YarnException(controller.getName() + "CGroup for " + pid + + " not found " + pidCgroupFile); + } + return result[0]; + } + + private void processFile(File file, Function processLine) + throws YarnException { + // Read "procfsDir//stat" file - typically /proc//stat + BufferedReader in = null; + InputStreamReader fReader = null; + try { + fReader = new InputStreamReader( + new FileInputStream( + file), Charset.forName("UTF-8")); + in = new BufferedReader(fReader); + } catch (FileNotFoundException f) { + throw new YarnException("The process vanished in the interim " + pid, f); + } + + try { + String str; + while ((str = in.readLine()) != null) { + Result result = processLine.apply(str); + if (result == Result.Exit) { + return; + } + } + } catch (IOException io) { + throw new YarnException("Error reading the stream " + io, io); + } finally { + // Close the streams + try { + fReader.close(); + try { + in.close(); + } catch (IOException i) { + LOG.warn("Error closing the stream " + in, i); + } + } catch (IOException i) { + LOG.warn("Error closing the stream " + fReader, i); + } + } + } + + private void setCGroupFilePaths() throws YarnException { + if (cGroupsHandler == null) { + throw new YarnException("CGroups handler is not initialized"); + } + File cpuDir = new File( + cGroupsHandler.getControllerPath( + CGroupsHandler.CGroupController.CPUACCT), + getCGroupRelativePathForPid(CGroupsHandler.CGroupController.CPUACCT)); + File memDir = new File( + cGroupsHandler.getControllerPath( + CGroupsHandler.CGroupController.MEMORY), + getCGroupRelativePathForPid(CGroupsHandler.CGroupController.MEMORY)); + cpuStat = new File(cpuDir, CPU_STAT); + memStat = new File(memDir, MEM_STAT); + memswStat = new File(memDir, MEMSW_STAT); + } +} 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/monitor/ContainersMonitorImpl.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/monitor/ContainersMonitorImpl.java index 55aac265d0e..9387793f704 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/monitor/ContainersMonitorImpl.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/monitor/ContainersMonitorImpl.java @@ -33,6 +33,7 @@ import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.event.AsyncDispatcher; import org.apache.hadoop.yarn.event.Dispatcher; +import org.apache.hadoop.yarn.exceptions.YarnException; import org.apache.hadoop.yarn.server.api.records.OverAllocationInfo; import org.apache.hadoop.yarn.server.api.records.ResourceThresholds; import org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor; @@ -41,6 +42,7 @@ import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.Container; import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.ContainerImpl; import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.ContainerKillEvent; +import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.resources.CGroupsResourceCalculator; import org.apache.hadoop.yarn.server.nodemanager.timelineservice.NMTimelinePublisher; import org.apache.hadoop.yarn.server.nodemanager.util.NodeManagerHardwareUtils; import org.apache.hadoop.yarn.util.ResourceCalculatorPlugin; @@ -552,9 +554,8 @@ private void initializeProcessTrees( LOG.debug("Tracking ProcessTree " + pId + " for the first time"); } ResourceCalculatorProcessTree pt = - ResourceCalculatorProcessTree. - getResourceCalculatorProcessTree( - pId, processTreeClass, conf); + getResourceCalculatorProcessTree( + pId); ptInfo.setPid(pId); ptInfo.setProcessTree(pt); @@ -588,6 +589,31 @@ private void initializeProcessTrees( } /** + * Get the best process tree calculator. + * @param pId container process id + * @return process tree calculator + */ + private ResourceCalculatorProcessTree + getResourceCalculatorProcessTree(String pId) { + ResourceCalculatorProcessTree pt = null; + + // CGroups is best in perforance, so try to use it, if it is enabled + if (processTreeClass == null && + CGroupsResourceCalculator.isAvailable()) { + try { + pt = new CGroupsResourceCalculator(pId); + } catch (YarnException e) { + LOG.info("CGroupsResourceCalculator cannot be created", e); + } + } + + pt = ResourceCalculatorProcessTree. + getResourceCalculatorProcessTree( + pId, processTreeClass, conf); + return pt; + } + + /** * Record usage metrics. * @param containerId container id * @param pId process id 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/TestCGroupsResourceCalculator.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/TestCGroupsResourceCalculator.java new file mode 100644 index 00000000000..bb3010ff86a --- /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/TestCGroupsResourceCalculator.java @@ -0,0 +1,167 @@ +package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.resources; + +import org.apache.commons.io.FileUtils; +import org.apache.hadoop.yarn.exceptions.YarnException; +import org.apache.hadoop.yarn.util.ControlledClock; +import org.apache.hadoop.yarn.util.ResourceCalculatorProcessTree; +import org.junit.Assert; +import org.junit.Test; + +import java.io.File; +import java.io.FileNotFoundException; + +import static org.mockito.Mockito.*; + +public class TestCGroupsResourceCalculator { + + ControlledClock clock = new ControlledClock(); + CGroupsHandler cGroupsHandler = mock(CGroupsHandler.class); + + @Test + public void testNoPid() throws Exception { + try { + CGroupsResourceCalculator calculator = + new CGroupsResourceCalculator( + "1234", ".", cGroupsHandler, clock); + } catch (YarnException e) { + Assert.assertTrue("Missing file should be caught", + e.getCause() instanceof FileNotFoundException); + } + } + + @Test + public void testNoMemoryCGgroupMount() throws Exception { + File procfs = new File("/tmp/" + this.getClass().getName() + "/1234"); + Assert.assertTrue("Setup error", procfs.mkdirs()); + try { + FileUtils.writeStringToFile( + new File(procfs, CGroupsResourceCalculator.CGROUP), + "7:devices:/yarn\n" + + "6:cpuacct,cpu:/yarn\n" + + "5:pids:/yarn\n"); + try { + CGroupsResourceCalculator calculator = + new CGroupsResourceCalculator( + "1234", "/tmp/" + this.getClass().getName(), + cGroupsHandler, clock); + } catch (YarnException e) { + Assert.assertTrue("Missing file should be caught", + e.getMessage().startsWith("memoryCGroup")); + } + } finally { + FileUtils.deleteDirectory(procfs); + } + } + + @Test + public void testNoCGgroup() throws Exception { + File procfs = new File("/tmp/" + this.getClass().getName() + "/1234"); + Assert.assertTrue("Setup error", procfs.mkdirs()); + try { + FileUtils.writeStringToFile( + new File(procfs, CGroupsResourceCalculator.CGROUP), + "7:devices:/yarn\n" + + "6:cpuacct,cpu:/yarn\n" + + "5:pids:/yarn\n" + + "4:memory:/yarn\n"); + try { + CGroupsResourceCalculator calculator = + new CGroupsResourceCalculator( + "1234", "/tmp/" + this.getClass().getName(), + cGroupsHandler, clock); + Assert.assertEquals("cgroups should be missing", + (long)ResourceCalculatorProcessTree.UNAVAILABLE, + calculator.getRssMemorySize(0)); + } catch (YarnException e) { + Assert.assertTrue("Missing file should be caught", + e.getMessage().startsWith("memoryCGroup")); + } + } finally { + FileUtils.deleteDirectory(procfs); + } + } + + @Test + public void testCPUParsing() throws Exception { + File cgcpuacctDir = + new File("/tmp/" + this.getClass().getName() + "/cgcpuacct/yarn"); + File procfs = new File("/tmp/" + this.getClass().getName() + "/1234"); + when(cGroupsHandler.getControllerPath( + CGroupsHandler.CGroupController.CPUACCT)). + thenReturn(cgcpuacctDir.getParent()); + Assert.assertTrue("Setup error", procfs.mkdirs()); + Assert.assertTrue("Setup error", cgcpuacctDir.mkdirs()); + try { + FileUtils.writeStringToFile( + new File(procfs, CGroupsResourceCalculator.CGROUP), + "7:devices:/yarn\n" + + "6:cpuacct,cpu:/yarn\n" + + "5:pids:/yarn\n" + + "4:memory:/yarn\n"); + FileUtils.writeStringToFile( + new File(cgcpuacctDir, CGroupsResourceCalculator.CPU_STAT), + "Can you handle this?\n" + + "user 5415\n" + + "system 3632"); + CGroupsResourceCalculator calculator = + new CGroupsResourceCalculator( + "1234", "/tmp/" + this.getClass().getName(), + cGroupsHandler, clock); + Assert.assertEquals("Incorrect CPU usage", + 90470, + calculator.getCumulativeCpuTime()); + } finally { + FileUtils.deleteDirectory(procfs); + FileUtils.deleteDirectory(cgcpuacctDir); + } + } + + @Test + public void testMemoryParsing() throws Exception { + File cgcpuacctDir = + new File("/tmp/" + this.getClass().getName() + "/cgcpuacct/yarn"); + File cgmemoryDir = + new File("/tmp/" + this.getClass().getName() + "/memory/yarn"); + File procfs = new File("/tmp/" + this.getClass().getName() + "/1234"); + when(cGroupsHandler.getControllerPath( + CGroupsHandler.CGroupController.MEMORY)). + thenReturn(cgmemoryDir.getParent()); + Assert.assertTrue("Setup error", procfs.mkdirs()); + Assert.assertTrue("Setup error", cgcpuacctDir.mkdirs()); + Assert.assertTrue("Setup error", cgmemoryDir.mkdirs()); + try { + FileUtils.writeStringToFile( + new File(procfs, CGroupsResourceCalculator.CGROUP), + "6:cpuacct,cpu:/yarn\n" + + "4:memory:/yarn\n"); + FileUtils.writeStringToFile( + new File(cgmemoryDir, CGroupsResourceCalculator.MEM_STAT), + "418496512\n"); + + CGroupsResourceCalculator calculator = + new CGroupsResourceCalculator( + "1234", "/tmp/" + this.getClass().getName(), + cGroupsHandler, clock); + + // Test the case where memsw is not available (Ubuntu) + Assert.assertEquals("Incorrect memory usage", + 418496512, + calculator.getRssMemorySize()); + Assert.assertEquals("Incorrect swap usage", + (long)ResourceCalculatorProcessTree.UNAVAILABLE, + calculator.getVirtualMemorySize()); + + // Test the case where memsw is available + FileUtils.writeStringToFile( + new File(cgmemoryDir, CGroupsResourceCalculator.MEMSW_STAT), + "418496513\n"); + Assert.assertEquals("Incorrect swap usage", + 418496513, + calculator.getVirtualMemorySize()); + } finally { + FileUtils.deleteDirectory(procfs); + FileUtils.deleteDirectory(cgcpuacctDir); + FileUtils.deleteDirectory(cgmemoryDir); + } + } +} From 043926ca77a21ea429b547c53bde6cce9b8a5c1a Mon Sep 17 00:00:00 2001 From: Miklos Szegedi Date: Tue, 6 Jun 2017 13:27:59 -0700 Subject: [PATCH 2/3] YARN-6668 Add new CGroupsResourceCalculator --- .../linux/resources/CGroupsResourceCalculator.java | 80 ++++++++++++------- .../monitor/ContainersMonitorImpl.java | 8 +- .../resources/TestCGroupsResourceCalculator.java | 91 +++++++++++----------- 3 files changed, 102 insertions(+), 77 deletions(-) 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/CGroupsResourceCalculator.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/CGroupsResourceCalculator.java index 466f7676e08..5b0400a6f51 100644 --- 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/CGroupsResourceCalculator.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/CGroupsResourceCalculator.java @@ -42,7 +42,6 @@ * A cgroups file-system based Resource calculator without the process tree * features. */ - public class CGroupsResourceCalculator extends ResourceCalculatorProcessTree { enum Result { Continue, @@ -71,8 +70,8 @@ private final CpuTimeTracker cpuTimeTracker; private Clock clock; - private long mem = UNAVAILABLE; - private long memSw = UNAVAILABLE; + private final static Object LOCK = new Object(); + private static boolean firstError = true; public CGroupsResourceCalculator(String pid) throws YarnException { this(pid, PROCFS, ResourceHandlerModule.getCGroupsHandler(), @@ -115,36 +114,18 @@ public long getCumulativeCpuTime() { @Override public long getRssMemorySize(int olderThanAge) { - if (olderThanAge > 0) { + if (olderThanAge > 1) { return 0; } - try { - processFile(memStat, (String line) -> { - mem = Long.parseLong(line); - return Result.Exit; - }); - return mem; - } catch (YarnException e) { - LOG.warn("Failed to parse cgroups " + memswStat, e); - } - return UNAVAILABLE; + return getMemorySize(memStat); } @Override public long getVirtualMemorySize(int olderThanAge) { - if (olderThanAge > 0) { + if (olderThanAge > 1) { return 0; } - try { - processFile(memswStat, (String line) -> { - memSw = Long.parseLong(line); - return Result.Exit; - }); - return memSw; - } catch (YarnException e) { - LOG.warn("Failed to parse cgroups " + memswStat, e); - } - return UNAVAILABLE; + return getMemorySize(memswStat); } @Override @@ -186,6 +167,25 @@ public static boolean isAvailable() { return true; } + private long getMemorySize(File cgroupUsageFile) { + long[] mem = new long[1]; + try { + processFile(cgroupUsageFile, (String line) -> { + mem[0] = Long.parseLong(line); + return Result.Exit; + }); + return mem[0]; + } catch (YarnException e) { + synchronized (LOCK) { + if (firstError) { + LOG.warn("Failed to parse cgroups " + memswStat, e); + firstError = false; + } + } + } + return UNAVAILABLE; + } + private void readTotalProcessJiffies() { try { final BigInteger[] totalCPUTimeJiffies = new BigInteger[1]; @@ -203,7 +203,12 @@ private void readTotalProcessJiffies() { }); processTotalJiffies = totalCPUTimeJiffies[0]; } catch (YarnException e) { - LOG.warn("Failed to parse " + pid, e); + synchronized (LOCK) { + if (firstError) { + LOG.warn("Failed to parse " + pid, e); + firstError = false; + } + } } } @@ -217,7 +222,22 @@ private String getCGroupRelativePathForPid( boolean mat = m.find(); if (mat) { if (m.group(2).contains(controller.getName())) { - result[0] = m.group(3); + // Instead of returning the full path we compose it + // based on the last item as the container id + // This helps to avoid confusion within a privileged Docker container + // where the path is referred in /proc//cgroup as + // /docker//hadoop-yarn/ + // but it is /hadoop-yarn/ in the cgroups hierarchy + String cgroupPath = m.group(3); + String cgroup = + new File(cgroupPath).toPath().getFileName().toString(); + + if (cgroup!=null && !cgroup.isEmpty()) { + result[0] = cGroupsHandler.getRelativePathForCGroup(cgroup); + } else { + LOG.warn("Invalid cgroup path " + cgroupPath + + " for " + pidCgroupFile); + } return Result.Exit; } } else { @@ -228,7 +248,7 @@ private String getCGroupRelativePathForPid( return Result.Continue; }); if (result[0] == null) { - throw new YarnException(controller.getName() + "CGroup for " + pid + + throw new YarnException(controller.getName() + " CGroup for pid " + pid + " not found " + pidCgroupFile); } return result[0]; @@ -237,8 +257,8 @@ private String getCGroupRelativePathForPid( private void processFile(File file, Function processLine) throws YarnException { // Read "procfsDir//stat" file - typically /proc//stat - BufferedReader in = null; - InputStreamReader fReader = null; + BufferedReader in; + InputStreamReader fReader; try { fReader = new InputStreamReader( new FileInputStream( 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/monitor/ContainersMonitorImpl.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/monitor/ContainersMonitorImpl.java index 9387793f704..159e08ac2a8 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/monitor/ContainersMonitorImpl.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/monitor/ContainersMonitorImpl.java @@ -607,9 +607,11 @@ private void initializeProcessTrees( } } - pt = ResourceCalculatorProcessTree. - getResourceCalculatorProcessTree( - pId, processTreeClass, conf); + if (pt == null) { + pt = ResourceCalculatorProcessTree. + getResourceCalculatorProcessTree( + pId, processTreeClass, conf); + } return pt; } 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/TestCGroupsResourceCalculator.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/TestCGroupsResourceCalculator.java index bb3010ff86a..4525b2e016b 100644 --- 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/TestCGroupsResourceCalculator.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/TestCGroupsResourceCalculator.java @@ -17,6 +17,10 @@ ControlledClock clock = new ControlledClock(); CGroupsHandler cGroupsHandler = mock(CGroupsHandler.class); + public TestCGroupsResourceCalculator() { + when(cGroupsHandler.getRelativePathForCGroup(any())).thenReturn("/yarn/container_1"); + } + @Test public void testNoPid() throws Exception { try { @@ -36,9 +40,9 @@ public void testNoMemoryCGgroupMount() throws Exception { try { FileUtils.writeStringToFile( new File(procfs, CGroupsResourceCalculator.CGROUP), - "7:devices:/yarn\n" + - "6:cpuacct,cpu:/yarn\n" + - "5:pids:/yarn\n"); + "7:devices:/yarn/container_1\n" + + "6:cpuacct,cpu:/yarn/container_1\n" + + "5:pids:/yarn/container_1\n"); try { CGroupsResourceCalculator calculator = new CGroupsResourceCalculator( @@ -46,10 +50,10 @@ public void testNoMemoryCGgroupMount() throws Exception { cGroupsHandler, clock); } catch (YarnException e) { Assert.assertTrue("Missing file should be caught", - e.getMessage().startsWith("memoryCGroup")); + e.getMessage().startsWith("memory CGroup")); } } finally { - FileUtils.deleteDirectory(procfs); + FileUtils.deleteDirectory(new File("/tmp/" + this.getClass().getName())); } } @@ -60,46 +64,44 @@ public void testNoCGgroup() throws Exception { try { FileUtils.writeStringToFile( new File(procfs, CGroupsResourceCalculator.CGROUP), - "7:devices:/yarn\n" + - "6:cpuacct,cpu:/yarn\n" + - "5:pids:/yarn\n" + - "4:memory:/yarn\n"); - try { - CGroupsResourceCalculator calculator = - new CGroupsResourceCalculator( - "1234", "/tmp/" + this.getClass().getName(), - cGroupsHandler, clock); - Assert.assertEquals("cgroups should be missing", - (long)ResourceCalculatorProcessTree.UNAVAILABLE, - calculator.getRssMemorySize(0)); - } catch (YarnException e) { - Assert.assertTrue("Missing file should be caught", - e.getMessage().startsWith("memoryCGroup")); - } + "7:devices:/yarn/container_1\n" + + "6:cpuacct,cpu:/yarn/container_1\n" + + "5:pids:/yarn/container_1\n" + + "4:memory:/yarn/container_1\n"); + + CGroupsResourceCalculator calculator = + new CGroupsResourceCalculator( + "1234", "/tmp/" + this.getClass().getName(), + cGroupsHandler, clock); + Assert.assertEquals("cgroups should be missing", + (long)ResourceCalculatorProcessTree.UNAVAILABLE, + calculator.getRssMemorySize(0)); } finally { - FileUtils.deleteDirectory(procfs); + FileUtils.deleteDirectory(new File("/tmp/" + this.getClass().getName())); } } @Test public void testCPUParsing() throws Exception { File cgcpuacctDir = - new File("/tmp/" + this.getClass().getName() + "/cgcpuacct/yarn"); + new File("/tmp/" + this.getClass().getName() + "/cgcpuacct"); + File cgcpuacctContainerDir = + new File(cgcpuacctDir, "/yarn/container_1"); File procfs = new File("/tmp/" + this.getClass().getName() + "/1234"); when(cGroupsHandler.getControllerPath( CGroupsHandler.CGroupController.CPUACCT)). - thenReturn(cgcpuacctDir.getParent()); + thenReturn(cgcpuacctDir.getAbsolutePath()); Assert.assertTrue("Setup error", procfs.mkdirs()); - Assert.assertTrue("Setup error", cgcpuacctDir.mkdirs()); + Assert.assertTrue("Setup error", cgcpuacctContainerDir.mkdirs()); try { FileUtils.writeStringToFile( new File(procfs, CGroupsResourceCalculator.CGROUP), - "7:devices:/yarn\n" + - "6:cpuacct,cpu:/yarn\n" + - "5:pids:/yarn\n" + - "4:memory:/yarn\n"); + "7:devices:/yarn/container_1\n" + + "6:cpuacct,cpu:/yarn/container_1\n" + + "5:pids:/yarn/container_1\n" + + "4:memory:/yarn/container_1\n"); FileUtils.writeStringToFile( - new File(cgcpuacctDir, CGroupsResourceCalculator.CPU_STAT), + new File(cgcpuacctContainerDir, CGroupsResourceCalculator.CPU_STAT), "Can you handle this?\n" + "user 5415\n" + "system 3632"); @@ -111,31 +113,34 @@ public void testCPUParsing() throws Exception { 90470, calculator.getCumulativeCpuTime()); } finally { - FileUtils.deleteDirectory(procfs); - FileUtils.deleteDirectory(cgcpuacctDir); + FileUtils.deleteDirectory(new File("/tmp/" + this.getClass().getName())); } } @Test public void testMemoryParsing() throws Exception { File cgcpuacctDir = - new File("/tmp/" + this.getClass().getName() + "/cgcpuacct/yarn"); + new File("/tmp/" + this.getClass().getName() + "/cgcpuacct"); + File cgcpuacctContainerDir = + new File(cgcpuacctDir, "/yarn/container_1"); File cgmemoryDir = - new File("/tmp/" + this.getClass().getName() + "/memory/yarn"); + new File("/tmp/" + this.getClass().getName() + "/memory"); + File cgMemoryContainerDir = + new File(cgmemoryDir, "/yarn/container_1"); File procfs = new File("/tmp/" + this.getClass().getName() + "/1234"); when(cGroupsHandler.getControllerPath( CGroupsHandler.CGroupController.MEMORY)). - thenReturn(cgmemoryDir.getParent()); + thenReturn(cgmemoryDir.getAbsolutePath()); Assert.assertTrue("Setup error", procfs.mkdirs()); - Assert.assertTrue("Setup error", cgcpuacctDir.mkdirs()); - Assert.assertTrue("Setup error", cgmemoryDir.mkdirs()); + Assert.assertTrue("Setup error", cgcpuacctContainerDir.mkdirs()); + Assert.assertTrue("Setup error", cgMemoryContainerDir.mkdirs()); try { FileUtils.writeStringToFile( new File(procfs, CGroupsResourceCalculator.CGROUP), - "6:cpuacct,cpu:/yarn\n" + - "4:memory:/yarn\n"); + "6:cpuacct,cpu:/yarn/container_1\n" + + "4:memory:/yarn/container_1\n"); FileUtils.writeStringToFile( - new File(cgmemoryDir, CGroupsResourceCalculator.MEM_STAT), + new File(cgMemoryContainerDir, CGroupsResourceCalculator.MEM_STAT), "418496512\n"); CGroupsResourceCalculator calculator = @@ -153,15 +158,13 @@ public void testMemoryParsing() throws Exception { // Test the case where memsw is available FileUtils.writeStringToFile( - new File(cgmemoryDir, CGroupsResourceCalculator.MEMSW_STAT), + new File(cgMemoryContainerDir, CGroupsResourceCalculator.MEMSW_STAT), "418496513\n"); Assert.assertEquals("Incorrect swap usage", 418496513, calculator.getVirtualMemorySize()); } finally { - FileUtils.deleteDirectory(procfs); - FileUtils.deleteDirectory(cgcpuacctDir); - FileUtils.deleteDirectory(cgmemoryDir); + FileUtils.deleteDirectory(new File("/tmp/" + this.getClass().getName())); } } } From 701b411f9ce8d2d489925853afc07d6153ca0a74 Mon Sep 17 00:00:00 2001 From: Miklos Szegedi Date: Mon, 19 Jun 2017 10:36:52 -0700 Subject: [PATCH 3/3] YARN-6668 Cgroups based resource calculator --- .../yarn/util/ResourceCalculatorProcessTree.java | 2 +- .../linux/resources/CGroupsResourceCalculator.java | 38 +++++++++- .../resources/TestCGroupsResourceCalculator.java | 85 +++++++++++++++++++++- 3 files changed, 119 insertions(+), 6 deletions(-) diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/ResourceCalculatorProcessTree.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/ResourceCalculatorProcessTree.java index adbba84767b..2512a772146 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/ResourceCalculatorProcessTree.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/ResourceCalculatorProcessTree.java @@ -37,7 +37,7 @@ @Public @Evolving public abstract class ResourceCalculatorProcessTree extends Configured { - static protected final Log LOG = LogFactory + protected static final Log LOG = LogFactory .getLog(ResourceCalculatorProcessTree.class); public static final int UNAVAILABLE = -1; 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/CGroupsResourceCalculator.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/CGroupsResourceCalculator.java index 5b0400a6f51..a8df076340a 100644 --- 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/CGroupsResourceCalculator.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/CGroupsResourceCalculator.java @@ -18,6 +18,7 @@ package org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.resources; +import com.google.common.annotations.VisibleForTesting; import org.apache.hadoop.util.CpuTimeTracker; import org.apache.hadoop.util.Shell; import org.apache.hadoop.util.SysInfoLinux; @@ -73,11 +74,34 @@ private final static Object LOCK = new Object(); private static boolean firstError = true; + /** + * Create resource calculator for all Yarn containers. + * @throws YarnException Could not access cgroups + */ + public CGroupsResourceCalculator() throws YarnException { + this(null, PROCFS, ResourceHandlerModule.getCGroupsHandler(), + SystemClock.getInstance()); + } + + /** + * Create resource calculator for the container that has the specified pid. + * @param pid A pid from the cgroup or null for all containers + * @throws YarnException Could not access cgroups + */ public CGroupsResourceCalculator(String pid) throws YarnException { this(pid, PROCFS, ResourceHandlerModule.getCGroupsHandler(), SystemClock.getInstance()); } + /** + * Create resource calculator for testing. + * @param pid A pid from the cgroup or null for all containers + * @param procfsDir Path to /proc or a mock /proc directory + * @param cGroupsHandler Initialized cgroups handler object + * @param clock A clock object + * @throws YarnException YarnException Could not access cgroups + */ + @VisibleForTesting CGroupsResourceCalculator(String pid, String procfsDir, CGroupsHandler cGroupsHandler, Clock clock) throws YarnException { @@ -212,6 +236,16 @@ private void readTotalProcessJiffies() { } } + private String getCGroupRelativePath( + CGroupsHandler.CGroupController controller) + throws YarnException { + if (pid == null) { + return cGroupsHandler.getRelativePathForCGroup(""); + } else { + return getCGroupRelativePathForPid(controller); + } + } + private String getCGroupRelativePathForPid( CGroupsHandler.CGroupController controller) throws YarnException { @@ -300,11 +334,11 @@ private void setCGroupFilePaths() throws YarnException { File cpuDir = new File( cGroupsHandler.getControllerPath( CGroupsHandler.CGroupController.CPUACCT), - getCGroupRelativePathForPid(CGroupsHandler.CGroupController.CPUACCT)); + getCGroupRelativePath(CGroupsHandler.CGroupController.CPUACCT)); File memDir = new File( cGroupsHandler.getControllerPath( CGroupsHandler.CGroupController.MEMORY), - getCGroupRelativePathForPid(CGroupsHandler.CGroupController.MEMORY)); + getCGroupRelativePath(CGroupsHandler.CGroupController.MEMORY)); cpuStat = new File(cpuDir, CPU_STAT); memStat = new File(memDir, MEM_STAT); memswStat = new File(memDir, MEMSW_STAT); 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/TestCGroupsResourceCalculator.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/TestCGroupsResourceCalculator.java index 4525b2e016b..1762cb9e6c3 100644 --- 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/TestCGroupsResourceCalculator.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/TestCGroupsResourceCalculator.java @@ -12,13 +12,18 @@ import static org.mockito.Mockito.*; +/** + * Unit test for CGroupsResourceCalculator. + */ public class TestCGroupsResourceCalculator { - ControlledClock clock = new ControlledClock(); - CGroupsHandler cGroupsHandler = mock(CGroupsHandler.class); + private ControlledClock clock = new ControlledClock(); + private CGroupsHandler cGroupsHandler = mock(CGroupsHandler.class); public TestCGroupsResourceCalculator() { - when(cGroupsHandler.getRelativePathForCGroup(any())).thenReturn("/yarn/container_1"); + when(cGroupsHandler.getRelativePathForCGroup("container_1")) + .thenReturn("/yarn/container_1"); + when(cGroupsHandler.getRelativePathForCGroup("")).thenReturn("/yarn/"); } @Test @@ -167,4 +172,78 @@ public void testMemoryParsing() throws Exception { FileUtils.deleteDirectory(new File("/tmp/" + this.getClass().getName())); } } + + @Test + public void testCPUParsingRoot() throws Exception { + File cgcpuacctDir = + new File("/tmp/" + this.getClass().getName() + "/cgcpuacct"); + File cgcpuacctRootDir = + new File(cgcpuacctDir, "/yarn"); + when(cGroupsHandler.getControllerPath( + CGroupsHandler.CGroupController.CPUACCT)). + thenReturn(cgcpuacctDir.getAbsolutePath()); + Assert.assertTrue("Setup error", cgcpuacctRootDir.mkdirs()); + try { + FileUtils.writeStringToFile( + new File(cgcpuacctRootDir, CGroupsResourceCalculator.CPU_STAT), + "user 5415\n" + + "system 3632"); + CGroupsResourceCalculator calculator = + new CGroupsResourceCalculator( + null, "/tmp/" + this.getClass().getName(), + cGroupsHandler, clock); + Assert.assertEquals("Incorrect CPU usage", + 90470, + calculator.getCumulativeCpuTime()); + } finally { + FileUtils.deleteDirectory(new File("/tmp/" + this.getClass().getName())); + } + } + + @Test + public void testMemoryParsingRoot() throws Exception { + File cgcpuacctDir = + new File("/tmp/" + this.getClass().getName() + "/cgcpuacct"); + File cgcpuacctRootDir = + new File(cgcpuacctDir, "/yarn"); + File cgmemoryDir = + new File("/tmp/" + this.getClass().getName() + "/memory"); + File cgMemoryRootDir = + new File(cgmemoryDir, "/yarn"); + File procfs = new File("/tmp/" + this.getClass().getName() + "/1234"); + when(cGroupsHandler.getControllerPath( + CGroupsHandler.CGroupController.MEMORY)). + thenReturn(cgmemoryDir.getAbsolutePath()); + Assert.assertTrue("Setup error", procfs.mkdirs()); + Assert.assertTrue("Setup error", cgcpuacctRootDir.mkdirs()); + Assert.assertTrue("Setup error", cgMemoryRootDir.mkdirs()); + try { + FileUtils.writeStringToFile( + new File(cgMemoryRootDir, CGroupsResourceCalculator.MEM_STAT), + "418496512\n"); + + CGroupsResourceCalculator calculator = + new CGroupsResourceCalculator( + null, "/tmp/" + this.getClass().getName(), + cGroupsHandler, clock); + + // Test the case where memsw is not available (Ubuntu) + Assert.assertEquals("Incorrect memory usage", + 418496512, + calculator.getRssMemorySize()); + Assert.assertEquals("Incorrect swap usage", + (long)ResourceCalculatorProcessTree.UNAVAILABLE, + calculator.getVirtualMemorySize()); + + // Test the case where memsw is available + FileUtils.writeStringToFile( + new File(cgMemoryRootDir, CGroupsResourceCalculator.MEMSW_STAT), + "418496513\n"); + Assert.assertEquals("Incorrect swap usage", + 418496513, + calculator.getVirtualMemorySize()); + } finally { + FileUtils.deleteDirectory(new File("/tmp/" + this.getClass().getName())); + } + } }