You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@uima.apache.org by cw...@apache.org on 2019/08/02 17:27:49 UTC
svn commit: r1864247 [5/10] - in
/uima/uima-ducc/trunk/uima-ducc-agent/src/main/java/org/apache/uima/ducc/agent:
./ config/ deploy/ deploy/uima/ event/ exceptions/ launcher/
metrics/collectors/ monitor/ processors/
Modified: uima/uima-ducc/trunk/uima-ducc-agent/src/main/java/org/apache/uima/ducc/agent/launcher/CGroupsManager.java
URL: http://svn.apache.org/viewvc/uima/uima-ducc/trunk/uima-ducc-agent/src/main/java/org/apache/uima/ducc/agent/launcher/CGroupsManager.java?rev=1864247&r1=1864246&r2=1864247&view=diff
==============================================================================
--- uima/uima-ducc/trunk/uima-ducc-agent/src/main/java/org/apache/uima/ducc/agent/launcher/CGroupsManager.java (original)
+++ uima/uima-ducc/trunk/uima-ducc-agent/src/main/java/org/apache/uima/ducc/agent/launcher/CGroupsManager.java Fri Aug 2 17:27:48 2019
@@ -45,1457 +45,1491 @@ import org.apache.uima.ducc.transport.ev
/**
* Manages cgroup container on a node
*
- * Supported operations: - cgcreate - creates cgroup container - cgset - sets
- * max memory limit for an existing container
+ * Supported operations: - cgcreate - creates cgroup container - cgset - sets max memory limit for
+ * an existing container
+ *
+ * This code supports both new and old cgconfig. The old configuration adds cgroups to <cgroup
+ * location>/ducc where the new adds it to: <cgroup location/cpu/ducc and <cgroup
+ * location>/memory/ducc. On startup the agent detects which cgconfig is active and adjusts
+ * accordingly.
*
- * This code supports both new and old cgconfig. The old configuration adds cgroups
- * to <cgroup location>/ducc where the new adds it to:
- * <cgroup location/cpu/ducc and <cgroup location>/memory/ducc.
- * On startup the agent detects which cgconfig is active and adjusts accordingly.
- *
*/
public class CGroupsManager {
- private DuccLogger agentLogger = null;
- private static final String SYSTEM = System.getProperty("user.name"); // Agent runs as the "ducc" user
- // the following three properties are only used for the new cgconfig
- private static final String CGDuccMemoryPath = "/memory/"+SYSTEM+"/";
- private static final String CGDuccCpuPath = "/cpu/"+SYSTEM+"/";
- private static final String CGProcsFile = "/cgroup.procs";
- // legacy means that the cgonfig points to <cgroup location>/ducc
- private boolean legacyCgConfig = false;
-
- private Object waitForLockObject = new Object();
-
- enum CGroupCommand {
- CGSET("cgset"),
- CGCREATE("cgcreate");
-
- String cmd;
- CGroupCommand(String cmd ) {
- this.cmd = cmd;
- }
- public String cmd() {
- return cmd;
- }
- };
- // manages list of 'active' cgroup containers by container id
- private Set<String> containerIds = new LinkedHashSet<String>();
- // stores cgroup base location
- private String cgroupBaseDir = "";
- // stores cgroup utils location like cgcreate, cgset, etc
- private String cgroupUtilsDir=null;
- // stores comma separated list of subsystems like cpu,memory
-
- private boolean cpuInfoSymlinked = true;
-
- private String cgroupSubsystems = ""; // comma separated list of subsystems
- private long retryMax = 4;
- private long delayFactor = 2000; // 2 secs in millis
- private long maxTimeToWaitForProcessToStop;
- private static String fetchCgroupsBaseDir(String mounts) {
- String cbaseDir=null;
- BufferedReader br = null;
- List<String> cgroupsEntries = new ArrayList<String>();
- try {
- List<String> lines = Files.readAllLines(Paths.get(mounts),Charset.defaultCharset());
- for( String line : lines ) {
- // trim the list to just cgroups
- if ( line.trim().startsWith("cgroup") ) {
- cgroupsEntries.add(line);
- }
- }
- // check if this is legacy cgroups installation looking like
- // cgroup /cgroup cgroup rw,relatime,memory,cpuacct,cpu 0 0
- if ( cgroupsEntries.size() == 1) {
- String[] cgroupsInfo = cgroupsEntries.get(0).split(" ");
- // return the mount point minus the memory part
- cbaseDir = cgroupsInfo[1].trim();
- } else {
- // check if this is recent cgroups installation looking like
- // cgroup /cgroup/memory cgroup rw,relatime,memory 0 0
- // OR
- // cgroup /sys/fs/cgroup/memory cgroup rw ...
-
- // return the mount point minus the memory part
- for(String line : cgroupsEntries) {
- String[] cgroupsInfo = line.split(" ");
- if ( cgroupsInfo[1].indexOf("/memory") != -1 ) {
- cbaseDir = cgroupsInfo[1].substring(0, cgroupsInfo[1].indexOf("/memory") );
- break;
- }
- }
- }
-
- } catch( Exception e) {
- e.printStackTrace();
- } finally {
- if ( br != null ) {
- try {
- br.close();
- } catch( Exception ex ) {}
- }
- }
- return cbaseDir;
- }
- /**
- * @param args
- */
- public static void main(String[] args) {
- String cgroupsUtilsDirs = "/usr/bin,/bin"; //System.getProperty("ducc.agent.launcher.cgroups.utils.dir");
- String cgUtilsPath = null;
- DuccLogger logger = DuccLogger.getLogger(CGroupsManager.class, "CGroupsManager");
- //boolean useCgroups = false;
-
- String[] paths = cgroupsUtilsDirs.split(",");
- for( String path : paths ) {
- File file = new File(path.trim()+"/cgexec");
- if ( file.exists() ) {
- cgUtilsPath = path;
- break;
- }
- }
-
- String cgroupsBaseDir = fetchCgroupsBaseDir("/proc/mounts");
-
- if ( cgUtilsPath == null ) {
- System.out.println("------- CGroups Disabled - Unable to Find Cgroups Utils Directory. Add/Modify ducc.agent.launcher.cgroups.utils.dir property in ducc.properties");
- } else if ( cgroupsBaseDir == null || cgroupsBaseDir.trim().length() == 0) {
- System.out.println("------- CGroups Disabled - Unable to Find Cgroups Root Directory in /proc/mounts");
-
- } else {
- System.out.println("Agent found cgroups runtime in "+cgUtilsPath+" cgroups base dir="+cgroupsBaseDir);
+ private DuccLogger agentLogger = null;
- String cgroupsSubsystems = "memory,cpu";
+ private static final String SYSTEM = System.getProperty("user.name"); // Agent runs as the "ducc"
+ // user
+ // the following three properties are only used for the new cgconfig
- long maxTimeToWaitForProcessToStop = 60000; // default 1 minute
+ private static final String CGDuccMemoryPath = "/memory/" + SYSTEM + "/";
- CGroupsManager cgroupsManager =
- new CGroupsManager(cgUtilsPath, cgroupsBaseDir, cgroupsSubsystems, logger, maxTimeToWaitForProcessToStop);
- // check if cgroups base directory exists in the filesystem
- // which means that cgroups
- // and cgroups convenience package are installed and the
- // daemon is up and running.
-
- try {
- if (cgroupsManager.cgroupExists(cgroupsBaseDir)) {
- System.out.println("Agent found cgroup base directory in "+cgroupsBaseDir);
+ private static final String CGDuccCpuPath = "/cpu/" + SYSTEM + "/";
+
+ private static final String CGProcsFile = "/cgroup.procs";
+
+ // legacy means that the cgonfig points to <cgroup location>/ducc
+ private boolean legacyCgConfig = false;
+
+ private Object waitForLockObject = new Object();
+
+ enum CGroupCommand {
+ CGSET("cgset"), CGCREATE("cgcreate");
+
+ String cmd;
+
+ CGroupCommand(String cmd) {
+ this.cmd = cmd;
+ }
+
+ public String cmd() {
+ return cmd;
+ }
+ };
+
+ // manages list of 'active' cgroup containers by container id
+ private Set<String> containerIds = new LinkedHashSet<String>();
+
+ // stores cgroup base location
+ private String cgroupBaseDir = "";
+
+ // stores cgroup utils location like cgcreate, cgset, etc
+ private String cgroupUtilsDir = null;
+ // stores comma separated list of subsystems like cpu,memory
+
+ private boolean cpuInfoSymlinked = true;
+
+ private String cgroupSubsystems = ""; // comma separated list of subsystems
+
+ private long retryMax = 4;
+
+ private long delayFactor = 2000; // 2 secs in millis
+
+ private long maxTimeToWaitForProcessToStop;
+
+ private static String fetchCgroupsBaseDir(String mounts) {
+ String cbaseDir = null;
+ BufferedReader br = null;
+ List<String> cgroupsEntries = new ArrayList<String>();
+ try {
+ List<String> lines = Files.readAllLines(Paths.get(mounts), Charset.defaultCharset());
+ for (String line : lines) {
+ // trim the list to just cgroups
+ if (line.trim().startsWith("cgroup")) {
+ cgroupsEntries.add(line);
+ }
+ }
+ // check if this is legacy cgroups installation looking like
+ // cgroup /cgroup cgroup rw,relatime,memory,cpuacct,cpu 0 0
+ if (cgroupsEntries.size() == 1) {
+ String[] cgroupsInfo = cgroupsEntries.get(0).split(" ");
+ // return the mount point minus the memory part
+ cbaseDir = cgroupsInfo[1].trim();
+ } else {
+ // check if this is recent cgroups installation looking like
+ // cgroup /cgroup/memory cgroup rw,relatime,memory 0 0
+ // OR
+ // cgroup /sys/fs/cgroup/memory cgroup rw ...
+
+ // return the mount point minus the memory part
+ for (String line : cgroupsEntries) {
+ String[] cgroupsInfo = line.split(" ");
+ if (cgroupsInfo[1].indexOf("/memory") != -1) {
+ cbaseDir = cgroupsInfo[1].substring(0, cgroupsInfo[1].indexOf("/memory"));
+ break;
+ }
+ }
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ if (br != null) {
+ try {
+ br.close();
+ } catch (Exception ex) {
+ }
+ }
+ }
+ return cbaseDir;
+ }
+
+ /**
+ * @param args
+ */
+ public static void main(String[] args) {
+ String cgroupsUtilsDirs = "/usr/bin,/bin"; // System.getProperty("ducc.agent.launcher.cgroups.utils.dir");
+ String cgUtilsPath = null;
+ DuccLogger logger = DuccLogger.getLogger(CGroupsManager.class, "CGroupsManager");
+ // boolean useCgroups = false;
+
+ String[] paths = cgroupsUtilsDirs.split(",");
+ for (String path : paths) {
+ File file = new File(path.trim() + "/cgexec");
+ if (file.exists()) {
+ cgUtilsPath = path;
+ break;
+ }
+ }
+
+ String cgroupsBaseDir = fetchCgroupsBaseDir("/proc/mounts");
+
+ if (cgUtilsPath == null) {
+ System.out.println(
+ "------- CGroups Disabled - Unable to Find Cgroups Utils Directory. Add/Modify ducc.agent.launcher.cgroups.utils.dir property in ducc.properties");
+ } else if (cgroupsBaseDir == null || cgroupsBaseDir.trim().length() == 0) {
+ System.out.println(
+ "------- CGroups Disabled - Unable to Find Cgroups Root Directory in /proc/mounts");
+
+ } else {
+ System.out.println("Agent found cgroups runtime in " + cgUtilsPath + " cgroups base dir="
+ + cgroupsBaseDir);
+
+ String cgroupsSubsystems = "memory,cpu";
+
+ long maxTimeToWaitForProcessToStop = 60000; // default 1 minute
+
+ CGroupsManager cgroupsManager = new CGroupsManager(cgUtilsPath, cgroupsBaseDir,
+ cgroupsSubsystems, logger, maxTimeToWaitForProcessToStop);
+ // check if cgroups base directory exists in the filesystem
+ // which means that cgroups
+ // and cgroups convenience package are installed and the
+ // daemon is up and running.
+
+ try {
+ if (cgroupsManager.cgroupExists(cgroupsBaseDir)) {
+ System.out.println("Agent found cgroup base directory in " + cgroupsBaseDir);
+
+ String containerId = "test";
+ // validate cgroups by creating a dummy cgroup. The code checks if cgroup actually got
+ // created by
+ // verifying existence of test cgroup file. The second step in verification is to check if
+ // CPU control is working. Configured in cgconfig.conf, the CPU control allows for setting
+ // cpu.shares. The code will attempt to set the shares and subsequently tries to read the
+ // value from cpu.shares file to make sure the values match. Any exception in the above
+ // steps
+ // will cause cgroups to be disabled.
+ //
+ cgroupsManager
+ .validator(cgroupsBaseDir, containerId, System.getProperty("user.name"), false)
+ .cgcreate().cgset(100); // write cpu.shares=100 and validate
+
+ // cleanup dummy cgroup
+ cgroupsManager.destroyContainer(containerId, System.getProperty("user.name"), -9);
+ }
+
+ } catch (Exception ee) {
+ ee.printStackTrace();
+ }
+ }
+ }
+
+ public CGroupsManager(String cgroupUtilsDir, String cgroupBaseDir, String subsystems,
+ DuccLogger agentLogger, long maxTimeToWaitForProcessToStop) {
+ this.cgroupUtilsDir = cgroupUtilsDir;
+ this.cgroupBaseDir = cgroupBaseDir;
+ this.cgroupSubsystems = subsystems;
+ this.agentLogger = agentLogger;
+ this.maxTimeToWaitForProcessToStop = maxTimeToWaitForProcessToStop;
+
+ // on some systems cpu and cpuacct may be linked to the same directory. In such
+ // cases we need adjust cgdelete command to only include memory,cpu as submodules:
+ //
+
+ // determine what cgroup base location should be. For legacy cgconfig
+ // it will be <cgroup folder>/ducc
+ try {
+ // check if the new (standard) cgconfig is active. It should have
+ // the following format <cgroup location>/memory
+ File f = new File(cgroupBaseDir + "/memory");
+ if (!f.exists()) {
+ // legacy cgconfig is active
+ this.cgroupBaseDir += "/" + SYSTEM + "/";
+ legacyCgConfig = true;
+ } else {
+ // new (standard) cgconfig is active
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ // if there is an error here, the new cgconfig is assumed and subject
+ // to additional testing on agent startup.
+ }
+ cpuInfoSymlinked = symbolicLinksForCpu();
+ String location = getCGroupLocation("cpuacct").trim();
+ if (!legacyCgConfig) {
+ if (!location.endsWith(System.getProperty("file.separator"))) {
+ location += System.getProperty("file.separator");
+ }
+ location += SYSTEM + System.getProperty("file.separator");
+ }
+ System.out.println("------------- Location:" + location);
+ /*
+ * if ( !legacyCgConfig ) { location = cgroupBaseDir;
+ * //SYSTEM+System.getProperty("file.separator"); if (
+ * !location.endsWith(System.getProperty("file.separator"))) { location = location +
+ * System.getProperty("file.separator"); } location +=
+ * SYSTEM+System.getProperty("file.separator"); } else { location =
+ * getCGroupLocation("cpuacct").trim(); if (
+ * !location.endsWith(System.getProperty("file.separator"))) { location = location +
+ * System.getProperty("file.separator"); }
+ *
+ * }
+ */
+
+ File cpuacctUsageFile = new File(location + "cpuacct.usage");
+ if (cpuacctUsageFile.exists()) {
+ System.out.println("Got cpuacct.usage file");
+ this.cgroupSubsystems += ",cpuacct";
+ }
+ }
+
+ /**
+ * Return location where cgroups utils like cgcreate can be found
+ *
+ * @return - absolute path to cgroup utils
+ */
+ public String getCGroupsUtilsDir() {
+ return cgroupUtilsDir;
+ }
+
+ /**
+ * Return cgroup base dir for legacy cgconfig or base dir/subsystem for the new cgconfig. The old
+ * looks like <cgroup folder>/ducc where the new looks like <cgroup folder>/memory
+ */
+ private String getCGroupLocation(String subsystem) {
+ String location = cgroupBaseDir.trim();
+
+ if (legacyCgConfig) {
+ return location;
+ } else if (!cgroupBaseDir.endsWith(System.getProperty("file.separator"))) {
+ location += System.getProperty("file.separator");
+ }
+ return location + subsystem;
+ }
+
+ private boolean isCpuSubmodule(String[] parts, String submoduleName) {
+ return (parts.length > 10 && parts[10].equals(submoduleName));
+ }
+
+ private boolean symbolicLinksForCpu() {
+ // File f = new File("/sys/fs/cgroup");
+ File f = new File(cgroupBaseDir);
+ if (!f.exists()) {
+ return false;
+ }
+ InputStreamReader isr = null;
+ BufferedReader reader = null;
+ try {
+ // String cmd[] = {"/usr/bin/ls","-l","/sys/fs/cgroup"};
+ String cmd[] = { "/bin/ls", "-l", cgroupBaseDir };
+ StringBuffer sb = new StringBuffer();
+ for (String s : cmd) {
+ sb.append(s).append(" ");
+ }
+ agentLogger.info("symbolicLinksForCpu", null,
+ "Launching Process - Commandline:" + sb.toString());
+
+ ProcessBuilder processLauncher = new ProcessBuilder();
+ processLauncher.command(cmd);
+ processLauncher.redirectErrorStream(true);
+ java.lang.Process process = processLauncher.start();
+ isr = new InputStreamReader(process.getInputStream());
+ reader = new BufferedReader(isr);
+ String line;
+ String cpuLinkDir = "";
+ String cpuacctLinkDir = "";
+
+ agentLogger.debug("symbolicLinksForCpu", null, "Consuming Process Streams");
+ while ((line = reader.readLine()) != null) {
+ agentLogger.info("symbolicLinksForCpu", null, ">>>>" + line);
+ // groupName = line.trim();
+ String parts[] = line.split(" ");
+ if (parts.length > 0 && parts[0].charAt(0) == 'l') { // link
+ if (isCpuSubmodule(parts, "cpu")) {
+ cpuLinkDir = parts[parts.length - 1];
+ } else if (isCpuSubmodule(parts, "cpuacct")) {
+ cpuacctLinkDir = parts[parts.length - 1];
+ }
+ // check if we got what we were looking for. If so, no need to iterate more
+ if (cpuLinkDir.length() > 0 && cpuacctLinkDir.length() > 0) {
+ break;
+ }
+ }
+ }
+
+ agentLogger.debug("symbolicLinksForCpu", null, "Waiting for Process to Exit");
+ int retCode = process.waitFor();
+ agentLogger.info("symbolicLinksForCpu", null, "Pocess Exit Code=" + retCode);
+ if (cpuLinkDir.length() > 0 && cpuacctLinkDir.length() > 0) {
+ if (cpuLinkDir.trim().equals(cpuLinkDir.trim())) {
+ return true; // both cpu and cpuacct link to the same dir
+ }
+ }
+
+ } catch (Exception e) {
+ agentLogger.error("symbolicLinksForCpu", null, e);
+
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (Exception e) {
+ }
+ }
+ }
+ return false;
+ }
+
+ public void configure(NodeAgent agent) {
+ if (agent != null) {
+ if (agent.configurationFactory.maxRetryCount != null) {
+ retryMax = Integer.valueOf(agent.configurationFactory.maxRetryCount);
+ }
+ if (agent.configurationFactory.retryDelayFactor != null) {
+ delayFactor = Integer.valueOf(agent.configurationFactory.retryDelayFactor);
+ }
+ }
+ }
- String containerId = "test";
- // validate cgroups by creating a dummy cgroup. The code checks if cgroup actually got created by
- // verifying existence of test cgroup file. The second step in verification is to check if
- // CPU control is working. Configured in cgconfig.conf, the CPU control allows for setting
- // cpu.shares. The code will attempt to set the shares and subsequently tries to read the
- // value from cpu.shares file to make sure the values match. Any exception in the above steps
- // will cause cgroups to be disabled.
- //
- cgroupsManager.validator(cgroupsBaseDir, containerId, System.getProperty("user.name"),false)
- .cgcreate()
- .cgset(100); // write cpu.shares=100 and validate
-
- // cleanup dummy cgroup
- cgroupsManager.destroyContainer(containerId, System.getProperty("user.name"), -9);
- }
-
- } catch( Exception ee) {
- ee.printStackTrace();
+ public Validator validator(String cgroupsBaseDir, String containerId, String userName,
+ boolean useDuccling) throws Exception {
+ return new Validator(this, getCGroupLocation("memory"), containerId, userName,
+ getUserGroupName(userName), useDuccling);
+ }
+
+ public String[] getPidsInCgroup(String cgroupName) throws Exception {
+
+ // File f = new File(cgroupBaseDir + CGDuccMemoryPath + cgroupName + CGProcsFile);
+ File f = new File(getCGroupLocation(CGDuccMemoryPath) + cgroupName + CGProcsFile);
+ // collect all pids
+ return readPids(f);
+ }
+
+ private String[] readPids(File f) throws Exception {
+ List<String> pids = new ArrayList<String>();
+ BufferedReader br = new BufferedReader(new FileReader(f));
+ String line;
+ while ((line = br.readLine()) != null) {
+ pids.add(line.trim());
+ }
+ br.close();
+ return pids.toArray(new String[pids.size()]);
+ }
+
+ /**
+ * Finds all stale CGroups and cleans them up. The code only cleans up cgroups folders with names
+ * that follow ducc's cgroup naming convention: <id>.<id>.<id>. First, each cgroup is checked for
+ * still running processes in the cgroup by looking at /<cgroup base dir>/<id>/cgroup.proc file
+ * which includes PIDs of processes associated with the cgroups. If processes are found, each one
+ * is killed via -9 and the cgroup is removed.
+ *
+ * @throws Exception
+ */
+ public void cleanup() throws Exception {
+
+ Set<NodeProcessInfo> processes = getProcessesOnNode();
+ // Match any folder under /cgroup/ducc that has syntax
+ // <number>.<number>.<number>
+ // This syntax is assigned by ducc to each cgroup
+ Pattern p = Pattern.compile("((\\d+)\\.(\\d+)\\.(\\d+))");
+
+ File cgroupsFolder = new File(getCGroupLocation(CGDuccMemoryPath));
+ String[] files = cgroupsFolder.list();
+ if (files == null || files.length == 0) {
+ return;
+ }
+
+ for (String cgroupFolder : files) {
+ Matcher m = p.matcher(cgroupFolder);
+ // only look at ducc's cgroups
+ if (m.find()) {
+ try {
+ // open proc file which may include PIDs if processes are
+ // still running
+ File f = new File(getCGroupLocation(CGDuccMemoryPath) + cgroupFolder + CGProcsFile);
+ // collect all pids
+ String[] pids = readPids(f);
+
+ if (pids != null && pids.length > 0) {
+ agentLogger.info("cleanupOnStartup", null, "Agent found " + pids.length
+ + " cgroup proceses still active. Proceeding to remove running processes");
+ }
+
+ int zombieCount = 0;
+ // kill each runnig process via -9
+ if (pids != null && pids.length > 0) {
+ for (String pid : pids) {
+ // Got cgroup processes still running. Kill them
+ for (NodeProcessInfo proc : processes) {
+ // Dont kill zombie process as it is already dead. Just increment how many of them
+ // we have
+ if (proc.isZombie()) {
+ zombieCount++;
+ } else if (proc.getPid().equals(pid)) {
+ // kill process hard via -9
+ kill(proc.getUserid(), proc.getPid(), NodeAgent.SIGKILL);
+ }
+ }
+ }
+ long logCount = 0;
+ // it may take some time for the cgroups to udate accounting. Just cycle until
+ // the procs file becomes empty under a given cgroup
+ while (true) {
+ pids = readPids(f);
+ // if the cgroup contains no pids or there are only zombie processes dont wait
+ // for cgroup accounting. These processes will never terminate. The idea
+ // is not to enter into an infinite loop due to zombies
+ if (pids == null || pids.length == 0 || (zombieCount == pids.length)) {
+ break;
+ } else {
+ try {
+ synchronized (this) {
+ // log every ~30 minutes (10000 * 200), where 200 is a wait time in ms between
+ // tries
+ if (logCount % 10000 == 0) {
+ agentLogger.info("cleanupOnStartup", null, "--- CGroup:" + cgroupFolder
+ + " procs file still showing processes running. Wait until CGroups updates acccounting");
+ }
+ logCount++;
+ wait(200);
+
+ }
+ } catch (InterruptedException ee) {
+ break;
+ }
+ }
+ }
+ }
+ // Don't remove CGroups if there are zombie processes there. Otherwise, attempt
+ // to remove the CGroup may hang a thread.
+ if (zombieCount == 0) { // no zombies in the container
+ destroyContainer(cgroupFolder, SYSTEM, NodeAgent.SIGTERM);
+ agentLogger.info("cleanupOnStartup", null,
+ "--- Agent Removed Empty CGroup:" + cgroupFolder);
+ } else {
+ agentLogger.info("cleanupOnStartup", null, "CGroup " + cgroupFolder
+ + " Contains Zombie Processing. Not Removing the Container");
+ }
+ } catch (FileNotFoundException e) {
+ // noop. Cgroup may have been removed already
+ } catch (Exception e) {
+ agentLogger.error("cleanupOnStartup", null, e);
+ }
+ }
+ }
+ }
+
+ private boolean isTargetForKill(Set<String> targets, String pid) {
+ Iterator<String> it = targets.iterator();
+ while (it.hasNext()) {
+ if (pid.equals(it.next())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Finds all stale CGroups and cleans them up. The code only cleans up cgroups folders with names
+ * that follow ducc's cgroup naming convention: <id>.<id>.<id>. First, each cgroup is checked for
+ * still running processes in the cgroup by looking at /<cgroup base dir>/<id>/cgroup.proc file
+ * which includes PIDs of processes associated with the cgroups. If processes are found, each one
+ * is killed via -9 and the cgroup is removed.
+ *
+ * @throws Exception
+ */
+ public void cleanupPids(Set<String> pidsToKill) throws Exception {
+
+ Set<NodeProcessInfo> processes = getProcessesOnNode();
+ // Match any folder under /cgroup/ducc that has syntax
+ // <number>.<number>.<number>
+ // This syntax is assigned by ducc to each cgroup
+ Pattern p = Pattern.compile("((\\d+)\\.(\\d+)\\.(\\d+))");
+
+ File cgroupsFolder = new File(getCGroupLocation(CGDuccMemoryPath));
+ String[] files = cgroupsFolder.list();
+ if (files == null || files.length == 0) {
+ return;
+ }
+
+ for (String cgroupFolder : files) {
+ Matcher m = p.matcher(cgroupFolder);
+ // only look at ducc's cgroups
+ if (m.find()) {
+ try {
+ // open proc file which may include PIDs if processes are
+ // still running
+ File f = new File(getCGroupLocation(CGDuccMemoryPath) + cgroupFolder + CGProcsFile);
+ // collect all pids
+ String[] pids = readPids(f);
+
+ if (pids != null && pids.length > 0) {
+ agentLogger.info("cleanupOnStartup", null, "Agent found " + pids.length
+ + " cgroup proceses still active. Proceeding to remove running processes");
+ }
+
+ int zombieCount = 0;
+ // kill each runnig process via -9
+ if (pids != null && pids.length > 0) {
+ for (String pid : pids) {
+ if (!isTargetForKill(pidsToKill, pid)) {
+ continue;
+ }
+ // Got cgroup processes still running. Kill them
+ for (NodeProcessInfo proc : processes) {
+ // Dont kill zombie process as it is already dead. Just increment how many of them
+ // we have
+ if (proc.isZombie()) {
+ zombieCount++;
+ } else if (proc.getPid().equals(pid)) {
+ // kill process hard via -9
+ System.out.println(">>>>>> Killing target process " + proc.getPid());
+ kill(proc.getUserid(), proc.getPid(), NodeAgent.SIGKILL);
+ }
}
+ }
+ long logCount = 0;
+ // it may take some time for the cgroups to udate accounting. Just cycle until
+ // the procs file becomes empty under a given cgroup
+ while (true) {
+ boolean found = false;
+ pids = readPids(f);
+ for (String pid : pids) {
+ if (isTargetForKill(pidsToKill, pid)) {
+ found = true;
+ break; // at least one process from the target list is still running
+ }
+ }
+
+ // if the cgroup contains no pids or there are only zombie processes dont wait
+ // for cgroup accounting. These processes will never terminate. The idea
+ // is not to enter into an infinite loop due to zombies
+ if (!found || pids == null || pids.length == 0 || (zombieCount == pids.length)) {
+ break;
+ } else {
+ try {
+ synchronized (this) {
+ // log every ~30 minutes (10000 * 200), where 200 is a wait time in ms between
+ // tries
+ if (logCount % 10000 == 0) {
+ agentLogger.info("cleanupOnStartup", null, "--- CGroup:" + cgroupFolder
+ + " procs file still showing processes running. Wait until CGroups updates acccounting");
+ }
+ logCount++;
+ wait(200);
+
+ }
+ } catch (InterruptedException ee) {
+ break;
+ }
+ }
+ }
+ }
+ // Don't remove CGroups if there are zombie processes there. Otherwise, attempt
+ // to remove the CGroup may hang a thread.
+ if (zombieCount == 0) { // no zombies in the container
+ destroyContainer(cgroupFolder, SYSTEM, NodeAgent.SIGTERM);
+ agentLogger.info("cleanupOnStartup", null,
+ "--- Agent Removed Empty CGroup:" + cgroupFolder);
+ } else {
+ agentLogger.info("cleanupOnStartup", null, "CGroup " + cgroupFolder
+ + " Contains Zombie Processing. Not Removing the Container");
+ }
+ } catch (FileNotFoundException e) {
+ // noop. Cgroup may have been removed already
+ } catch (Exception e) {
+ agentLogger.error("cleanupOnStartup", null, e);
}
- }
- public CGroupsManager(String cgroupUtilsDir, String cgroupBaseDir, String subsystems,
- DuccLogger agentLogger, long maxTimeToWaitForProcessToStop) {
- this.cgroupUtilsDir = cgroupUtilsDir;
- this.cgroupBaseDir = cgroupBaseDir;
- this.cgroupSubsystems = subsystems;
- this.agentLogger = agentLogger;
- this.maxTimeToWaitForProcessToStop = maxTimeToWaitForProcessToStop;
-
- // on some systems cpu and cpuacct may be linked to the same directory. In such
- // cases we need adjust cgdelete command to only include memory,cpu as submodules:
- //
-
-
-
-
- // determine what cgroup base location should be. For legacy cgconfig
- // it will be <cgroup folder>/ducc
- try {
- // check if the new (standard) cgconfig is active. It should have
- // the following format <cgroup location>/memory
- File f = new File(cgroupBaseDir+"/memory");
- if ( !f.exists()) {
- // legacy cgconfig is active
- this.cgroupBaseDir += "/"+SYSTEM+"/";
- legacyCgConfig = true;
- } else {
- // new (standard) cgconfig is active
- }
- } catch(Exception e) {
- e.printStackTrace();
- // if there is an error here, the new cgconfig is assumed and subject
- // to additional testing on agent startup.
- }
- cpuInfoSymlinked = symbolicLinksForCpu();
- String location = getCGroupLocation("cpuacct").trim();
- if ( !legacyCgConfig ) {
- if (!location.endsWith(System.getProperty("file.separator") )) {
- location += System.getProperty("file.separator");
- }
- location += SYSTEM+System.getProperty("file.separator");
- }
- System.out.println("------------- Location:"+location);
- /*
- if ( !legacyCgConfig ) {
- location = cgroupBaseDir; //SYSTEM+System.getProperty("file.separator");
- if ( !location.endsWith(System.getProperty("file.separator"))) {
- location = location + System.getProperty("file.separator");
- }
- location += SYSTEM+System.getProperty("file.separator");
- } else {
- location = getCGroupLocation("cpuacct").trim();
- if ( !location.endsWith(System.getProperty("file.separator"))) {
- location = location + System.getProperty("file.separator");
- }
-
- }
-*/
-
-
- File cpuacctUsageFile = new File(location+"cpuacct.usage");
- if ( cpuacctUsageFile.exists()) {
- System.out.println("Got cpuacct.usage file");
- this.cgroupSubsystems += ",cpuacct";
- }
- }
-
- /**
- * Return location where cgroups utils like cgcreate can be found
- *
- * @return - absolute path to cgroup utils
- */
- public String getCGroupsUtilsDir( ){
- return cgroupUtilsDir;
- }
- /**
- * Return cgroup base dir for legacy cgconfig or base dir/subsystem
- * for the new cgconfig. The old looks like <cgroup folder>/ducc where
- * the new looks like <cgroup folder>/memory
- */
- private String getCGroupLocation(String subsystem) {
- String location = cgroupBaseDir.trim();
-
- if ( legacyCgConfig ) {
- return location;
- } else if (!cgroupBaseDir.endsWith(System.getProperty("file.separator") )) {
- location += System.getProperty("file.separator");
- }
- return location + subsystem;
- }
- private boolean isCpuSubmodule(String[] parts, String submoduleName) {
- return ( parts.length > 10 && parts[10].equals(submoduleName) );
- }
- private boolean symbolicLinksForCpu() {
-// File f = new File("/sys/fs/cgroup");
- File f = new File(cgroupBaseDir);
- if ( !f.exists()) {
- return false;
- }
- InputStreamReader isr = null;
- BufferedReader reader = null;
- try {
-// String cmd[] = {"/usr/bin/ls","-l","/sys/fs/cgroup"};
- String cmd[] = {"/bin/ls","-l",cgroupBaseDir};
- StringBuffer sb = new StringBuffer();
- for (String s : cmd) {
- sb.append(s).append(" ");
- }
- agentLogger.info("symbolicLinksForCpu", null, "Launching Process - Commandline:"+sb.toString());
-
- ProcessBuilder processLauncher = new ProcessBuilder();
- processLauncher.command(cmd);
- processLauncher.redirectErrorStream(true);
- java.lang.Process process = processLauncher.start();
- isr = new InputStreamReader(process.getInputStream());
- reader = new BufferedReader(isr);
- String line;
- String cpuLinkDir = "";
- String cpuacctLinkDir = "";
-
- agentLogger.debug("symbolicLinksForCpu", null, "Consuming Process Streams");
- while ((line = reader.readLine()) != null) {
- agentLogger.info("symbolicLinksForCpu", null, ">>>>" + line);
- //groupName = line.trim();
- String parts[] = line.split(" ");
- if ( parts.length > 0 && parts[0].charAt(0)=='l') { // link
- if ( isCpuSubmodule(parts,"cpu") ) {
- cpuLinkDir = parts[parts.length-1];
- } else if (isCpuSubmodule(parts,"cpuacct") ) {
- cpuacctLinkDir = parts[parts.length-1];
- }
- // check if we got what we were looking for. If so, no need to iterate more
- if ( cpuLinkDir.length() > 0 && cpuacctLinkDir.length() > 0 ) {
- break;
- }
- }
- }
-
- agentLogger.debug("symbolicLinksForCpu", null, "Waiting for Process to Exit");
- int retCode = process.waitFor();
- agentLogger.info("symbolicLinksForCpu", null, "Pocess Exit Code="+retCode);
- if ( cpuLinkDir.length() > 0 && cpuacctLinkDir.length() > 0) {
- if ( cpuLinkDir.trim().equals(cpuLinkDir.trim())) {
- return true; // both cpu and cpuacct link to the same dir
- }
- }
-
- } catch( Exception e) {
- agentLogger.error("symbolicLinksForCpu", null, e);
-
- } finally {
- if ( reader != null ) {
- try {
- reader.close();
- } catch(Exception e) {}
- }
- }
- return false;
- }
- public void configure(NodeAgent agent ) {
- if ( agent != null ) {
- if ( agent.configurationFactory.maxRetryCount != null ) {
- retryMax = Integer.valueOf(agent.configurationFactory.maxRetryCount);
- }
- if ( agent.configurationFactory.retryDelayFactor != null ) {
- delayFactor = Integer.valueOf(agent.configurationFactory.retryDelayFactor);
- }
- }
- }
- public Validator validator( String cgroupsBaseDir,String containerId, String userName, boolean useDuccling) throws Exception {
- return new Validator(this, getCGroupLocation("memory"), containerId, userName, getUserGroupName(userName),useDuccling);
- }
- public String[] getPidsInCgroup(String cgroupName) throws Exception {
-
-// File f = new File(cgroupBaseDir + CGDuccMemoryPath + cgroupName + CGProcsFile);
- File f = new File(getCGroupLocation(CGDuccMemoryPath)+ cgroupName + CGProcsFile);
- // collect all pids
- return readPids(f);
- }
- private String[] readPids(File f) throws Exception {
- List<String> pids = new ArrayList<String>();
- BufferedReader br = new BufferedReader(new FileReader(f));
- String line;
- while ((line = br.readLine()) != null) {
- pids.add(line.trim());
- }
- br.close();
- return pids.toArray(new String[pids.size()]);
- }
- /**
- * Finds all stale CGroups and cleans them up. The code only
- * cleans up cgroups folders with names that follow
- * ducc's cgroup naming convention: <id>.<id>.<id>.
- * First, each cgroup is checked for still running processes in the
- * cgroup by looking at /<cgroup base dir>/<id>/cgroup.proc file which
- * includes PIDs of processes associated with the cgroups. If
- * processes are found, each one is killed via -9 and the cgroup
- * is removed.
- *
- * @throws Exception
- */
- public void cleanup() throws Exception {
-
- Set<NodeProcessInfo> processes = getProcessesOnNode();
- // Match any folder under /cgroup/ducc that has syntax
- // <number>.<number>.<number>
- // This syntax is assigned by ducc to each cgroup
- Pattern p = Pattern.compile("((\\d+)\\.(\\d+)\\.(\\d+))");
-
- File cgroupsFolder = new File(getCGroupLocation(CGDuccMemoryPath));
- String[] files = cgroupsFolder.list();
- if ( files == null || files.length == 0 ) {
- return;
- }
-
- for (String cgroupFolder : files) {
- Matcher m = p.matcher(cgroupFolder);
- // only look at ducc's cgroups
- if (m.find()) {
- try {
- // open proc file which may include PIDs if processes are
- // still running
- File f = new File(getCGroupLocation(CGDuccMemoryPath) + cgroupFolder+ CGProcsFile);
- // collect all pids
- String[] pids = readPids(f);
-
- if ( pids != null && pids.length > 0 ) {
- agentLogger.info("cleanupOnStartup", null,"Agent found "+pids.length+" cgroup proceses still active. Proceeding to remove running processes");
- }
-
- int zombieCount=0;
- // kill each runnig process via -9
- if (pids != null && pids.length > 0) {
- for (String pid : pids) {
- // Got cgroup processes still running. Kill them
- for (NodeProcessInfo proc : processes) {
- // Dont kill zombie process as it is already dead. Just increment how many of them we have
- if ( proc.isZombie() ) {
- zombieCount++;
- } else if (proc.getPid().equals(pid)) {
- // kill process hard via -9
- kill( proc.getUserid(), proc.getPid(), NodeAgent.SIGKILL);
- }
- }
- }
- long logCount = 0;
- // it may take some time for the cgroups to udate accounting. Just cycle until
- // the procs file becomes empty under a given cgroup
- while( true ) {
- pids = readPids(f);
- // if the cgroup contains no pids or there are only zombie processes dont wait
- // for cgroup accounting. These processes will never terminate. The idea
- // is not to enter into an infinite loop due to zombies
- if ( pids == null || pids.length == 0 || (zombieCount == pids.length)) {
- break;
- } else {
- try {
- synchronized(this) {
- // log every ~30 minutes (10000 * 200), where 200 is a wait time in ms between tries
- if ( logCount % 10000 == 0) {
- agentLogger.info("cleanupOnStartup", null,
- "--- CGroup:" + cgroupFolder+ " procs file still showing processes running. Wait until CGroups updates acccounting");
- }
- logCount++;
- wait(200);
-
- }
- } catch( InterruptedException ee) {
- break;
- }
- }
- }
- }
- // Don't remove CGroups if there are zombie processes there. Otherwise, attempt
- // to remove the CGroup may hang a thread.
- if ( zombieCount == 0 ) { // no zombies in the container
- destroyContainer(cgroupFolder, SYSTEM, NodeAgent.SIGTERM);
- agentLogger.info("cleanupOnStartup", null,
- "--- Agent Removed Empty CGroup:" + cgroupFolder);
- } else {
- agentLogger.info("cleanupOnStartup", null,"CGroup "+cgroupFolder+" Contains Zombie Processing. Not Removing the Container");
- }
- } catch (FileNotFoundException e) {
- // noop. Cgroup may have been removed already
- } catch (Exception e) {
- agentLogger.error("cleanupOnStartup", null, e);
- }
- }
- }
- }
- private boolean isTargetForKill(Set<String> targets, String pid) {
- Iterator<String> it = targets.iterator();
- while( it.hasNext() ) {
- if (pid.equals(it.next())) {
- return true;
- }
- }
- return false;
- }
- /**
- * Finds all stale CGroups and cleans them up. The code only
- * cleans up cgroups folders with names that follow
- * ducc's cgroup naming convention: <id>.<id>.<id>.
- * First, each cgroup is checked for still running processes in the
- * cgroup by looking at /<cgroup base dir>/<id>/cgroup.proc file which
- * includes PIDs of processes associated with the cgroups. If
- * processes are found, each one is killed via -9 and the cgroup
- * is removed.
- *
- * @throws Exception
- */
- public void cleanupPids(Set<String> pidsToKill) throws Exception {
-
- Set<NodeProcessInfo> processes = getProcessesOnNode();
- // Match any folder under /cgroup/ducc that has syntax
- // <number>.<number>.<number>
- // This syntax is assigned by ducc to each cgroup
- Pattern p = Pattern.compile("((\\d+)\\.(\\d+)\\.(\\d+))");
-
- File cgroupsFolder = new File(getCGroupLocation(CGDuccMemoryPath));
- String[] files = cgroupsFolder.list();
- if ( files == null || files.length == 0 ) {
- return;
- }
-
- for (String cgroupFolder : files) {
- Matcher m = p.matcher(cgroupFolder);
- // only look at ducc's cgroups
- if (m.find()) {
- try {
- // open proc file which may include PIDs if processes are
- // still running
- File f = new File(getCGroupLocation(CGDuccMemoryPath) + cgroupFolder+ CGProcsFile);
- // collect all pids
- String[] pids = readPids(f);
-
- if ( pids != null && pids.length > 0 ) {
- agentLogger.info("cleanupOnStartup", null,"Agent found "+pids.length+" cgroup proceses still active. Proceeding to remove running processes");
- }
-
- int zombieCount=0;
- // kill each runnig process via -9
- if (pids != null && pids.length > 0) {
- for (String pid : pids) {
- if ( !isTargetForKill(pidsToKill, pid)) {
- continue;
- }
- // Got cgroup processes still running. Kill them
- for (NodeProcessInfo proc : processes) {
- // Dont kill zombie process as it is already dead. Just increment how many of them we have
- if ( proc.isZombie() ) {
- zombieCount++;
- } else if (proc.getPid().equals(pid)) {
- // kill process hard via -9
- System.out.println(">>>>>> Killing target process "+proc.getPid());
- kill( proc.getUserid(), proc.getPid(), NodeAgent.SIGKILL);
- }
- }
- }
- long logCount = 0;
- // it may take some time for the cgroups to udate accounting. Just cycle until
- // the procs file becomes empty under a given cgroup
- while( true ) {
- boolean found = false;
- pids = readPids(f);
- for ( String pid : pids ) {
- if ( isTargetForKill(pidsToKill, pid)) {
- found = true;
- break; // at least one process from the target list is still running
- }
- }
-
- // if the cgroup contains no pids or there are only zombie processes dont wait
- // for cgroup accounting. These processes will never terminate. The idea
- // is not to enter into an infinite loop due to zombies
- if ( !found || pids == null || pids.length == 0 || (zombieCount == pids.length)) {
- break;
- } else {
- try {
- synchronized(this) {
- // log every ~30 minutes (10000 * 200), where 200 is a wait time in ms between tries
- if ( logCount % 10000 == 0) {
- agentLogger.info("cleanupOnStartup", null,
- "--- CGroup:" + cgroupFolder+ " procs file still showing processes running. Wait until CGroups updates acccounting");
- }
- logCount++;
- wait(200);
-
- }
- } catch( InterruptedException ee) {
- break;
- }
- }
- }
- }
- // Don't remove CGroups if there are zombie processes there. Otherwise, attempt
- // to remove the CGroup may hang a thread.
- if ( zombieCount == 0 ) { // no zombies in the container
- destroyContainer(cgroupFolder, SYSTEM, NodeAgent.SIGTERM);
- agentLogger.info("cleanupOnStartup", null,
- "--- Agent Removed Empty CGroup:" + cgroupFolder);
- } else {
- agentLogger.info("cleanupOnStartup", null,"CGroup "+cgroupFolder+" Contains Zombie Processing. Not Removing the Container");
- }
- } catch (FileNotFoundException e) {
- // noop. Cgroup may have been removed already
- } catch (Exception e) {
- agentLogger.error("cleanupOnStartup", null, e);
- }
- }
- }
- }
-
- public boolean isPidInCGroup(String pid) throws Exception {
- String[] pids = getAllCGroupPids();
- for( String p : pids ) {
- if ( p.equals(pid)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Returns an array of PIDs managed by cgroups.
- *
- * @return - String array of PIDs
- * @throws Exception
- */
+ }
+ }
+ }
+
+ public boolean isPidInCGroup(String pid) throws Exception {
+ String[] pids = getAllCGroupPids();
+ for (String p : pids) {
+ if (p.equals(pid)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns an array of PIDs managed by cgroups.
+ *
+ * @return - String array of PIDs
+ * @throws Exception
+ */
public String[] getAllCGroupPids() throws Exception {
- List<String> cgroupPids = new ArrayList<String>();
-
- // Match any folder under <cgroup base dir> that has syntax
- // <number>.<number>.<number>
- // This syntax is assigned by ducc to each cgroup
- Pattern p = Pattern.compile("((\\d+)\\.(\\d+)\\.(\\d+))");
-
- File cgroupsFolder = new File(getCGroupLocation(CGDuccMemoryPath));
- String[] files = cgroupsFolder.list();
- if ( files == null || files.length == 0 ) {
- return new String[0]; // empty better than NULL
- }
- for (String cgroupFolder : files) {
- Matcher m = p.matcher(cgroupFolder);
- // only look at ducc's cgroups
- if (m.find()) {
- try {
- // open proc file which may include PIDs if processes are
- // still running
- File f = new File(getCGroupLocation(CGDuccMemoryPath) + cgroupFolder+ CGProcsFile);
-
- // collect all pids
- String[] pids = readPids(f);
- for( String pid : pids ) {
- cgroupPids.add(pid);
- }
- } catch (Exception e) {
- agentLogger.error("getAllCGroupPids", null, e);
- throw e;
- }
- }
- }
- String[] pids = new String[cgroupPids.size()];
- return cgroupPids.toArray(pids);
- }
-
- public void kill(final String user, final String pid, final int signal) {
- final String methodName = "kill";
- String c_launcher_path="";
- try {
-
- boolean useDuccling = false;
- String useSpawn = System.getProperty("ducc.agent.launcher.use.ducc_spawn");
- if (useSpawn != null && useSpawn.toLowerCase().equals("true")) {
- useDuccling = true;
- c_launcher_path = Utils.resolvePlaceholderIfExists(
- System.getProperty("ducc.agent.launcher.ducc_spawn_path"),
- System.getProperties());
- }
-
- String cmdLine;
- String arg;
- if (Utils.isWindows()) {
- cmdLine = "taskkill";
- arg = "/PID";
- } else {
- cmdLine = "/bin/kill";
- arg = "-"+signal;
- }
-
- String[] command;
- if (useDuccling) {
- command = new String[] { c_launcher_path, "-u", user,
- "--", cmdLine, arg, pid };
- } else {
- command = new String[] { cmdLine, arg, pid };
- }
-
- launchCommand(command);
-
- StringBuffer sb = new StringBuffer();
- for (String part : command) {
- sb.append(part).append(" ");
- }
- if (agentLogger == null) {
- System.out.println("--------- Killed Process:" + pid
- + " Owned by:" + user + " Command:" + sb.toString());
-
- } else {
- agentLogger.info(methodName, null,
- "--------- Killed CGroup Process:" + pid + " Owned by:" + user
- + " Command:" + sb.toString());
- }
- } catch (Exception e) {
- agentLogger.error(methodName, null,e );
- }
- }
- public String getContainerId(ManagedProcess managedProcess) {
- String containerId;
- if ( managedProcess.getDuccProcess().getProcessType().equals(ProcessType.Service)) {
- containerId = String.valueOf(managedProcess.getDuccProcess().getCGroup().getId());
- } else {
- containerId = managedProcess.getWorkDuccId().getFriendly()+"."+managedProcess.getDuccProcess().getCGroup().getId();
- }
- return containerId;
- }
- public String getDuccUid() {
- return SYSTEM;
- }
-
- /**
- * Creates cgroup container with a given id and owner.
- *
- * @param containerId
- * - new cgroup container id
- * @param userId
- * - owner of the cgroup container
- * @param useDuccSpawn
- * - use duccling to run 'cgcreate' command
- *
- * @return - true on success, false otherwise
- *
- * @throws Exception
- */
- public boolean createContainer(String containerId, String userName, String groupName,
- boolean useDuccSpawn) throws Exception {
- String message = "";
- agentLogger.info("createContainer", null, "Creating CGroup Container:" + containerId);
- String[] command = new String[] { cgroupUtilsDir+"/cgcreate", "-t",
- userName+":"+groupName, "-a", userName+":"+groupName, "-g",
- cgroupSubsystems + ":"+SYSTEM+"/" + containerId };
- int retCode = launchCommand(command);
- // first fetch the location of cgroups on this system. If cgroups is configured
- // with newer cgconfig add 'memory' to the base dir
- if ( cgroupExists(getCGroupLocation(CGDuccMemoryPath) + containerId)) {
- // Starting with libcgroup v.0.38, the cgcreate fails
- // with exit code = 96 even though the cgroup gets
- // created! The following code treats such return code
- // as success. In case there is an error, subsequent
- // cgset or cgexec will fail.
- if (retCode == 0 || retCode == 96) {
- containerIds.add(containerId);
- agentLogger.info("createContainer", null, ">>>>"
- + "SUCCESS - Created CGroup Container:" + containerId+". The cgcreate return code:"+retCode);
- return true;
- }
- } else {
- message = ">>> CGroup Container:"+containerId+ " not found in "+getCGroupLocation(CGDuccMemoryPath)+containerId;
- }
- agentLogger.error("createContainer", null, message);
- System.out.println(message);
- return false;
- }
- private String composeCpuAccountingFileName(String id) {
- String location = getCGroupLocation("cpuacct").trim();
- if ( !location.endsWith(System.getProperty("file.separator"))) {
- location = location + System.getProperty("file.separator");
- }
- if ( !legacyCgConfig ) {
- location += SYSTEM+System.getProperty("file.separator");
- }
- return location+id+"cpuacct.usage";
- }
- private String composeMemoryStatFileName(String id) {
- String location = getCGroupLocation("memory").trim();
- if ( !location.endsWith(System.getProperty("file.separator"))) {
- location = location + System.getProperty("file.separator");
- }
- if ( !legacyCgConfig ) {
- location += SYSTEM+System.getProperty("file.separator");
- }
- return location+id+"memory.stat";
- }
-
- public boolean isCpuReportingEnabled() {
-// String file = getCGroupLocation("cpuacct")+System.getProperty("file.separator")+"cpuacct.usage";
-
- File f = new File(composeCpuAccountingFileName(""));
- System.out.println(f.getAbsolutePath());
- return f.exists();
- }
- public long getCpuUsage(String containerId ) throws Exception {
- long usage = 0;
-
- if (!containerId.endsWith(System.getProperty("file.separator"))) {
- containerId = containerId + System.getProperty("file.separator");
- }
- String file = composeCpuAccountingFileName(containerId.trim());
- agentLogger.debug("getCpuUsage", null, "CPUACCT.USAGE file:"+file);
- File f = new File(file);
- if ( f.exists() ) {
- InputStreamReader isr = new InputStreamReader(new FileInputStream(f));
- BufferedReader br = new BufferedReader(isr);
- String line;
- try {
- while ((line = br.readLine()) != null) {
- agentLogger.trace("getCpuUsage", null, "CPUACCT.USAGE Line:"+line);
- usage = Long.parseLong(line.trim());
- break;
- }
- } catch ( Exception e) {
- agentLogger.error("getCpuUsage", null, e);
- }
- finally {
- if (isr != null) {
- isr.close();
- }
- agentLogger.trace("getCpuUsage", null, "Done Reading cpuacct.stat file:"+file);
-
- }
- } else {
- agentLogger.info("getCpuUsage", null, "CPUACCT.USAGE file:"+file+" Not Found - Process CPU Usage is Unavailable");
-
- usage = -1; // cgroups accounting not configured
- }
- return usage;
-
- }
- public enum CgroupMemoryStat {
- RSS("rss"),
- SWAP("swap"),
- FAULTS("pgpgin");
-
- String key;
-
- private CgroupMemoryStat(String aKey) {
- this.key = aKey;
- }
-
- public String getKey() {
- return this.key;
- }
-
- }
- public long getUsageForMemoryStat(CgroupMemoryStat stat, String containerId ) throws Exception {
- long usage = -1;
-
- if (!containerId.endsWith(System.getProperty("file.separator"))) {
- containerId = containerId + System.getProperty("file.separator");
- }
- String file = composeMemoryStatFileName(containerId.trim());
- agentLogger.debug("getUsageForMemoryStat", null, "MEMORY.STAT file:"+file);
- File f = new File(file);
- if ( f.exists() ) {
- InputStreamReader isr = new InputStreamReader(new FileInputStream(f));
- BufferedReader br = new BufferedReader(isr);
- String line;
- try {
- while ((line = br.readLine()) != null) {
- agentLogger.debug("getUsageForMemoryStat", null, "MEMORY.STAT Line:"+line);
- if ( line.startsWith(stat.getKey())) {
- usage = Long.parseLong(line.trim().split(" ")[1]);
- break;
- }
- }
- } catch ( Exception e) {
- agentLogger.error("getUsageForMemoryStat", null, e);
- }
- finally {
- if (isr != null) {
- isr.close();
- }
- agentLogger.debug("getUsageForMemoryStat", null, "Done Reading memory.stat file:"+file);
- }
- } else {
- agentLogger.debug("getUsageForMemoryStat", null, "MEMORY.STAT file:"+file+" Not Found - Process RSS Usage is Unavailable");
-
- usage = -1; // cgroups accounting not configured
- }
- return usage;
- }
-
-
- /**
- * Sets the max memory use for an existing cgroup container.
- *
- * @param containerId
- * - existing container id for which limit will be set
- * @param userId
- * - container owner
- * @param useDuccSpawn
- * - run 'cgset' command as a user
- * @param containerMaxSize
- * - max memory limit
- *
- * @return - true on success, false otherwise
- *
- * @throws Exception
- */
-
- public boolean setContainerMaxMemoryLimit(String containerId,
- String userId, boolean useDuccSpawn, long containerMaxSize)
- throws Exception {
- try {
- String[] command = new String[] { cgroupUtilsDir+"/cgset", "-r",
- "memory.limit_in_bytes=" + containerMaxSize,
- SYSTEM+"/" + containerId };
- int retCode = launchCommand(command);
- if (retCode == 0) {
- agentLogger.debug("setContainerMaxMemoryLimit", null, ">>>>"
- + "SUCCESS - Created CGroup Limit on Container:"
- + containerId);
- return true;
- } else {
- agentLogger.info("setContainerMaxMemoryLimit", null, ">>>>"
- + "FAILURE - Unable To Create CGroup Container:"
- + containerId);
- return false;
- }
- } catch (Exception e) {
- agentLogger.error("setContainerMaxMemoryLimit", null, ">>>>"
- + "FAILURE - Unable To Set Limit On CGroup Container:"
- + containerId, e);
- return false;
- }
- }
- /**
- * Sets the cpu shares use for an existing cgroup container.
- *
- * @param containerId
- * - existing container id for which limit will be set
- * @param userId
- * - container owner
- * @param useDuccSpawn
- * - run 'cgset' command as a user
- * @param containerCpuShares
- * - cpu shares
- *
- * @return - true on success, false otherwise
- *
- * @throws Exception
- */
-
- public boolean setContainerCpuShares(String containerId,
- String userId, boolean useDuccSpawn, long containerCpuShares)
- throws Exception {
- try {
- String[] command = new String[] { cgroupUtilsDir+"/cgset", "-r",
- "cpu.shares=" + containerCpuShares,
- SYSTEM+"/" + containerId };
- int retCode = launchCommand(command);
- if (retCode == 0) {
- agentLogger.debug("setContainerCpuShares", null, ">>>>"
- + "SUCCESS - Created CGroup with CPU Shares="+containerCpuShares+" on Container:"
- + containerId);
- return true;
- } else {
- agentLogger.info("setContainerCpuShares", null, ">>>>"
- + "FAILURE - Unable To Set CPU shares on CGroup Container:"
- + containerId);
- return false;
- }
- } catch (Exception e) {
- agentLogger.error("setContainerCpuShares", null, ">>>>"
- + "FAILURE - Unable To Set CPU shares On CGroup Container:"
- + containerId, e);
- return false;
- }
- }
-
- /**
- * Sets the memory swappiness for an existing cgroup container.
- *
- * @param containerId
- * - existing container id for which limit will be set
- * @param userId
- * - container owner
- * @param useDuccSpawn
- * - run 'cgset' command as a user
- * @param swappiness
- * - swappiness
- *
- * @return - true on success, false otherwise
- *
- * @throws Exception
- */
-
- public boolean setContainerSwappiness(String containerId,
- String userId, boolean useDuccSpawn, long swappiness)
- throws Exception {
- try {
- String[] command = new String[] { cgroupUtilsDir+"/cgset", "-r",
- "memory.swappiness=" + swappiness,
- SYSTEM + "/" + containerId };
- int retCode = launchCommand(command);
- if (retCode == 0) {
- agentLogger.info("setContainerSwappiness", null, ">>>>"
- + "SUCCESS - Updated CGroup with Memory Swappiness="+swappiness+" on Container:"
- + containerId);
- return true;
- } else {
- agentLogger.info("setContainerSwappiness", null, ">>>>"
- + "FAILURE - Unable To Set Swappiness on CGroup Container:"
- + containerId);
- return false;
- }
- } catch (Exception e) {
- agentLogger.error("setContainerSwappiness", null, ">>>>"
- + "FAILURE - Unable To Set Swappiness On CGroup Container:"
- + containerId, e);
- return false;
- }
- }
- private int killChildProcesses(String containerId, String userId, int signal) throws Exception {
- int childCount=0;
- String[] pids = getPidsInCgroup(containerId);
- if ( pids != null ) {
- if ( pids.length > 0 ) {
- childCount = pids.length;
- agentLogger.info("killChildProcesses", null,"Found "+pids.length+" child processes still in container:"+containerId+" - killing all");
- }
- for( String pid : pids ) {
- try {
- kill(userId, pid, signal);
- } catch(Exception ee) {
- agentLogger.warn("killChildProcesses", null, "Unable to kill child process with PID:"+pid+" from cgroup:"+containerId+"\n"+ee);
- }
- }
- }
- return childCount;
- }
- private String adjustSubsystems() {
-
-
- // if cpu and cpuacct are sym linked to the same dir, remove cpuacct part
- // from the submodule list as it causes the cgdelete to throw an error.
- if ( cpuInfoSymlinked && cgroupSubsystems.indexOf(",cpuacct") > -1 ) {
- return cgroupSubsystems.substring(0,cgroupSubsystems.indexOf(",cpuacct") );
-
-
- /*
- StringBuffer sb = new StringBuffer();
- if ( cgroupSubsystems.indexOf("cpuacct") > -1 ) {
- String[] subsystems = cgroupSubsystems.trim().split(",");
- //StringUtils.j
- Iterator<String> it = Arrays.asList(subsystems).iterator();
- if ( it.hasNext()) {
- while(it.hasNext() ) {
- String subsystem = it.next();
- if ( !"cpuacct".equals(subsystem)) {
- sb.append(subsystem);
- if ( it.hasNext()) {
- sb.append(",");
- }
- }
- }
- }
- return sb.toString();
- }
- */
- }
-
- return cgroupSubsystems;
- }
- /**
- * Removes cgroup container with a given id. Cgroups are implemented as a
- * virtual file system. All is needed here is just rmdir.
- *
- * @param containerId
- * - cgroup to remove
- * @return - true on success, false otherwise
- *
- * @throws Exception
- */
- public boolean destroyContainer(String containerId, String userId, int signal) throws Exception {
- try {
- if (cgroupExists(getCGroupLocation(CGDuccMemoryPath) + containerId)) {
- if ( signal == NodeAgent.SIGTERM ) {
- agentLogger.info("destroyContainer", null, "Destroying Container "+containerId+" Using signal:"+signal +" to kill child processes if any still exist in cgroups container");
-
- // before removing cgroup container, make sure to kill
- // all processes that still may be there. User process
- // may have created child processes that may still be running.
- // First use kill -15, than wait and any process still standing
- // will be killed hard via kill -9
- int childProcessCount =
- killChildProcesses(containerId, userId, NodeAgent.SIGTERM);
- if ( childProcessCount > 0 ) {
- agentLogger.info("destroyContainer", null, "Killed "+childProcessCount+"Child Processes with kill -15");
- synchronized( waitForLockObject) {
- try {
- waitForLockObject.wait(maxTimeToWaitForProcessToStop);
- } catch( InterruptedException ie) {
- }
- }
- }
- }
- // Any process remaining in a cgroup will be killed hard
- killChildProcesses(containerId, userId, NodeAgent.SIGKILL);
- //String subsystems =cgroupSubsystems.substring(0,cgroupSubsystems.indexOf(",cpuacct") );
- String subsystems = adjustSubsystems();
-
- String[] command = new String[] { cgroupUtilsDir+"/cgdelete",subsystems + ":"+SYSTEM+"/" + containerId };
- int retCode = launchCommand(command);
- if ( cgroupExists(getCGroupLocation(CGDuccMemoryPath) + containerId)) {
- agentLogger.info("destroyContainer", null, "Failed to remove Container "+containerId+" Using cgdelete command. Exit code:"+retCode);
- return false;
- } else {
- containerIds.remove(containerId);
- return true;
- }
- }
- return true; // nothing to do, cgroup does not exist
- } catch (Exception e) {
- agentLogger.info("destroyContainer", null, e);
- return false;
- }
- }
- public String getUserGroupName(String userName) throws Exception {
- String groupName="";
- InputStreamReader isr = null;
- BufferedReader reader = null;
- try {
- String cmd[] = {"/usr/bin/id","-g","-n",userName};;//System.getProperty("user.name")};
- StringBuffer sb = new StringBuffer();
- for (String s : cmd) {
- sb.append(s).append(" ");
- }
- agentLogger.info("getuserGroupName", null, "Launching Process - Commandline:"+sb.toString());
-
- ProcessBuilder processLauncher = new ProcessBuilder();
- processLauncher.command(cmd);
- processLauncher.redirectErrorStream(true);
- java.lang.Process process = processLauncher.start();
- isr = new InputStreamReader(process.getInputStream());
- reader = new BufferedReader(isr);
- String line;
- agentLogger.info("getUserGroupName", null, "Consuming Process Streams");
- while ((line = reader.readLine()) != null) {
- agentLogger.info("getUserGroupName", null, ">>>>" + line);
- System.out.println(line);
- groupName = line.trim();
- }
- agentLogger.info("getUserGroupName", null, "Waiting for Process to Exit");
- int retCode = process.waitFor();
- agentLogger.info("getUserGroupName", null, "Pocess Exit Code="+retCode);
-
- } catch( Exception e) {
- agentLogger.error("getUserGroupName", null, e);
-
- } finally {
- if ( reader != null ) {
- reader.close();
- }
- }
- return groupName;
- }
- private int launchCommand(String[] command/*, String userId*/) throws Exception {
-
- int retryCount=0;
- Object sleepMonitor = new Object();
- if ( command == null) {
- return -1;
- }
- synchronized(CGroupsManager.class) {
- long delay = delayFactor;//
- while( retryCount <= retryMax ) {
- String message = "";
- InputStreamReader in = null;
- BufferedReader reader = null;
- StringBuffer sb = new StringBuffer();
- if ( command != null ) {
- for (int i = 0; i < command.length; i++) {
- sb.append(command[i]).append(" ");
- }
- }
-
- try {
- agentLogger.info("launchCommand", null, "Launching Process - Commandline:"+sb.toString());
- ProcessBuilder processLauncher = new ProcessBuilder();
-
- processLauncher.command(command);
- processLauncher.redirectErrorStream(true);
- java.lang.Process process = processLauncher.start();
-
- in = new InputStreamReader(
- process.getInputStream());
- reader = new BufferedReader(in);
- String line;
- agentLogger.info("launchCommand", null, "Consuming Process Streams");
- while ((line = reader.readLine()) != null) {
- // per team discussin 6/23/ dont need to log "Operation not permitted"
- // which is logged by cgcreate erroneously. The cgroup is actually created
- // but cgcreate still dumps this msg to stdout. If we log this, a user
- // may get confused. If thercannot remove groupe is a legitimate problem a subsequent test
- // for existence of cgroup will catch a missing cgroup and report it as
- // error.
- if ( line.indexOf("Operation not permitted") > -1 ) {
- continue; // dont log if the above string is in the stdout stream
- } else if ( line.indexOf("cannot remove group") > -1 ) {
- continue; // could be false positive. Validation will catch if unable to remove
- }
- agentLogger.info("launchCommand", null, ">>>>" + line);
- System.out.println(line);
- }
- agentLogger.info("launchCommand", null, "Waiting for Process to Exit");
- int retCode = process.waitFor();
-
- // Starting with libcgroup v.0.38, the cgcreate fails
- // with exit code = 96 even though the cgroup gets
- // created! The following code treats such return code
- // as success. In case there is an error, subsequent
- // cgset or cgexec will fail.
- if (retCode == 0 || retCode == 96) {
- System.out.println("--------- Returning Code:"+retCode+" Command:"+sb.toString());
-
- return retCode;
- } else {
- message = ">>>>"
- + "FAILURE - return code:"+retCode+" Unable To exec command:"+sb.toString()
- + " Retrying in "+delay+" millis - retry#"+(retryCount+1);
- }
-
- } catch (Exception e) {
- e.printStackTrace();
- message = ">>>>"
- + "FAILURE - Unable To exec command:"+sb.toString()
- +" Retrying in "+delay+" millis - retry#"+(retryCount+1);
- } finally {
- if ( reader != null ) {
- try {
- reader.close();
- } catch( Exception exx) {}
- }
- }
- if ( retryMax == 0 ) {
- agentLogger.error("launchCommand", null, ">>>>"
- + "Not configured to retry command:"+sb.toString());
- break;
- }
- agentLogger.error("launchCommand", null, message);
- System.out.println(message);
- try {
- synchronized(sleepMonitor) {
- sleepMonitor.wait(delay);
- }
- } catch( InterruptedException ie) {}
-
- retryCount++;
- delay += delayFactor;
- } // while
-
-
- }
- return -1; // failure
- }
-
- /**
- * Return a Set of existing cgroup Ids found in the filesystem identified by
- * 'cgroupBaseDir'.
- *
- * @return - set of cgroup ids
- *
- * @throws Exception
- */
- public Set<String> collectExistingContainers() throws Exception {
-// File duccCGroupBaseDir = new File(cgroupBaseDir);
- File duccCGroupBaseDir = new File(getCGroupLocation(CGDuccMemoryPath));
- if (duccCGroupBaseDir.exists()) {
- File[] existingCGroups = duccCGroupBaseDir.listFiles();
- if ( existingCGroups != null ) {
- for (File cgroup : existingCGroups) {
- if (cgroup.isDirectory()) {
- containerIds.add(cgroup.getName());
- }
- }
- }
- }
- return containerIds;
- }
-
- public String getDuccCGroupBaseDir() {
- return cgroupBaseDir;
- }
-
- // UIMA-5405 Include the installed "ducc" id
- public String getSubsystems() {
- return cgroupSubsystems + ":" + SYSTEM + "/";
- }
-
- public boolean cgroupExists(String cgroup) throws Exception {
- File duccCGroupBaseDir = new File(cgroup);
- return duccCGroupBaseDir.exists();
- }
-
- public Set<NodeProcessInfo> getProcessesOnNode() throws Exception {
- String location = "getProcessesOnNode";
- Set<NodeProcessInfo> processList = new HashSet<NodeProcessInfo>();
- InputStream stream = null;
- BufferedReader reader = null;
- try {
-
- ProcessBuilder pb = new ProcessBuilder("ps", "-Ao",
- "user:32,pid,ppid,args,stat", "--no-heading");
- pb.redirectErrorStream(true);
- java.lang.Process proc = pb.start();
- // spawn ps command and scrape the output
- stream = proc.getInputStream();
- reader = new BufferedReader(new InputStreamReader(
- stream));
- String line;
- String regex = "\\s+";
-
- // read the next line from ps output
- while ((line = reader.readLine()) != null) {
-
- String tokens[] = line.split(regex);
- String user = tokens[0];
- String pid = tokens[1];
- String ppid = tokens[2];
- String stat = tokens[4];
-
- if (tokens.length > 0) {
-
- processList.add(new NodeProcessInfo(pid, ppid, user, stat));
- }
- }
- } catch (Exception e) {
- if (agentLogger == null) {
- e.printStackTrace();
- } else {
- agentLogger.error(location, null, e);
- }
- } finally {
- if ( reader != null ) {
- reader.close();
- }
- }
- return processList;
-
- }
-
- public class NodeProcessInfo {
- private String pid;
- private String ppid;
- private String userid;
- private String stat;
-
- NodeProcessInfo(String pid, String ppid, String uid, String stat) {
- this.pid = pid;
- this.ppid = ppid;
- this.userid = uid;
- this.stat = stat;
- }
-
- public boolean isZombie() {
- return (stat == "Z") ? true : false;
- }
- public String getPid() {
- return pid;
- }
-
- public String getPpid() {
- return ppid;
- }
-
- public String getUserid() {
- return userid;
- }
-
- public void setUserid(String userid) {
- this.userid = userid;
- }
-
- }
- public class CGroupsException extends RuntimeException {
- private static final long serialVersionUID = 1L;
- private String command;
- private String msg;
-
- public CGroupsException() {
- }
- public CGroupsException(Exception e) {
- super(e);
- }
- public CGroupsException addCommand(String command) {
- this.command = command;
- return this;
- }
- public CGroupsException addMessage(String msg) {
- this.msg = msg;
- return this;
- }
- public String getCommand() {
- return command;
- }
- public String getMessage() {
- return msg;
- }
-
- }
- public class Validator {
- private CGroupsManager cgmgr=null;
- String containerId;
- String userName;
- String userGroupName;
- boolean useDuccling;
- String cgroupsBaseDir;
-
-
-
- Validator(CGroupsManager instance, String cgroupsBaseDir,String containerId, String uid, String usergroup, boolean useDuccling) {
- cgmgr = instance;
- this.containerId = containerId;
- this.userName= uid;
- this.useDuccling = useDuccling;
- this.userGroupName = usergroup;
- this.cgroupsBaseDir = cgroupsBaseDir;
- }
- public Validator cgcreate() throws CGroupsException {
- String msg1 = "------- CGroups cgcreate failed to create a cgroup - disabling cgroups";
- String msg2 = "------- CGroups cgcreate failed to validate a cgroup - disabling cgroups";
- String msg3 = "------- CGroups cgcreate failed - disabling cgroups";
- try {
-
- if ( !cgmgr.createContainer(containerId, userName, userGroupName, useDuccling) ) {
- throw new CGroupsException().addCommand(CGroupCommand.CGCREATE.cmd())
- .addMessage(msg1);
- }
-// if (!cgmgr.cgroupExists(cgroupsBaseDir + "/memory/ducc/" + containerId)) {
- if (!cgmgr.cgroupExists(getCGroupLocation(CGDuccMemoryPath)+ containerId)) {
- throw new CGroupsException().addCommand(CGroupCommand.CGCREATE.cmd())
- .addMessage(msg2);
- }
- } catch( Exception e) {
- throw new CGroupsException(e).addCommand(CGroupCommand.CGCREATE.cmd())
- .addMessage(msg3);
- }
- return this;
- }
- public Validator cgset( long cpuShares) throws CGroupsException {
- String msg1 = "------- Check cgconfig.conf CPU control. The cgset failed to set cpu.shares";
- String msg2 = "------- Check cgconfig.conf CPU control. The cgset failed to find cpu.shares file";
- String msg3 = "------- Check cgconfig.conf CPU control. The cgset failed to write to cpu.shares file. Expected 100 shares found ";
-
- BufferedReader reader = null;
- String shares = "";
- try {
- if (!cgmgr.setContainerCpuShares(containerId, userName, useDuccling, cpuShares) ) {
- throw new CGroupsException().addCommand(CGroupCommand.CGSET.cmd())
- .addMessage(msg1);
- }
- // now try to read created file
-// File f = new File(cgroupsBaseDir + "/cpu/ducc/" + "test/cpu.shares");
- File f = new File(getCGroupLocation(CGDuccCpuPath)+ containerId+"/cpu.shares");
- reader = new BufferedReader(new FileReader(f));
- // read 1st line. It should be equal to cpuShares
- if ( reader != null ) {
- shares = reader.readLine();
- if ( shares != null ) {
- shares = shares.trim();
- }
- }
- System.out.println("----- Cgroup cgset verifier - cpu.shares read from file:"+shares);
- if ( !String.valueOf(cpuShares).equals(shares)) {
- throw new CGroupsException().addCommand(CGroupCommand.CGSET.cmd())
- .addMessage(msg3+shares);
- }
- } catch( FileNotFoundException e ) {
- //e.printStackTrace();
- throw new CGroupsException(e).addCommand(CGroupCommand.CGSET.cmd())
- .addMessage(msg2);
- } catch(Exception e) {
- //e.printStackTrace();
- throw new CGroupsException(e).addCommand(CGroupCommand.CGSET.cmd())
- .addMessage(msg3+shares);
-
- } finally {
- if ( reader != null ) {
- try {
- reader.close();
- } catch( Exception ee) {}
- }
- }
- return this;
- }
- }
+ List<String> cgroupPids = new ArrayList<String>();
+
+ // Match any folder under <cgroup base dir> that has syntax
+ // <number>.<number>.<number>
+ // This syntax is assigned by ducc to each cgroup
+ Pattern p = Pattern.compile("((\\d+)\\.(\\d+)\\.(\\d+))");
+
+ File cgroupsFolder = new File(getCGroupLocation(CGDuccMemoryPath));
+ String[] files = cgroupsFolder.list();
+ if (files == null || files.length == 0) {
+ return new String[0]; // empty better than NULL
+ }
+ for (String cgroupFolder : files) {
+ Matcher m = p.matcher(cgroupFolder);
+ // only look at ducc's cgroups
+ if (m.find()) {
+ try {
+ // open proc file which may include PIDs if processes are
+ // still running
+ File f = new File(getCGroupLocation(CGDuccMemoryPath) + cgroupFolder + CGProcsFile);
+
+ // collect all pids
+ String[] pids = readPids(f);
+ for (String pid : pids) {
+ cgroupPids.add(pid);
+ }
+ } catch (Exception e) {
+ agentLogger.error("getAllCGroupPids", null, e);
+ throw e;
+ }
+ }
+ }
+ String[] pids = new String[cgroupPids.size()];
+ return cgroupPids.toArray(pids);
+ }
+
+ public void kill(final String user, final String pid, final int signal) {
+ final String methodName = "kill";
+ String c_launcher_path = "";
+ try {
+
+ boolean useDuccling = false;
+ String useSpawn = System.getProperty("ducc.agent.launcher.use.ducc_spawn");
+ if (useSpawn != null && useSpawn.toLowerCase().equals("true")) {
+ useDuccling = true;
+ c_launcher_path = Utils.resolvePlaceholderIfExists(
+ System.getProperty("ducc.agent.launcher.ducc_spawn_path"), System.getProperties());
+ }
+
+ String cmdLine;
+ String arg;
+ if (Utils.isWindows()) {
+ cmdLine = "taskkill";
+ arg = "/PID";
+ } else {
+ cmdLine = "/bin/kill";
+ arg = "-" + signal;
+ }
+
+ String[] command;
+ if (useDuccling) {
+ command = new String[] { c_launcher_path, "-u", user, "--", cmdLine, arg, pid };
+ } else {
+ command = new String[] { cmdLine, arg, pid };
+ }
+
+ launchCommand(command);
+
+ StringBuffer sb = new StringBuffer();
+ for (String part : command) {
+ sb.append(part).append(" ");
+ }
+ if (agentLogger == null) {
+ System.out.println("--------- Killed Process:" + pid + " Owned by:" + user + " Command:"
+ + sb.toString());
+
+ } else {
+ agentLogger.info(methodName, null, "--------- Killed CGroup Process:" + pid + " Owned by:"
+ + user + " Command:" + sb.toString());
+ }
+ } catch (Exception e) {
+ agentLogger.error(methodName, null, e);
+ }
+ }
+
+ public String getContainerId(ManagedProcess managedProcess) {
+ String containerId;
+ if (managedProcess.getDuccProcess().getProcessType().equals(ProcessType.Service)) {
+ containerId = String.valueOf(managedProcess.getDuccProcess().getCGroup().getId());
+ } else {
+ containerId = managedProcess.getWorkDuccId().getFriendly() + "."
+ + managedProcess.getDuccProcess().getCGroup().getId();
+ }
+ return containerId;
+ }
+
+ public String getDuccUid() {
+ return SYSTEM;
+ }
+
+ /**
+ * Creates cgroup container with a given id and owner.
+ *
+ * @param containerId
+ * - new cgroup container id
+ * @param userId
+ * - owner of the cgroup container
+ * @param useDuccSpawn
+ * - use duccling to run 'cgcreate' command
+ *
+ * @return - true on success, false otherwise
+ *
+ * @throws Exception
+ */
+ public boolean createContainer(String containerId, String userName, String groupName,
+ boolean useDuccSpawn) throws Exception {
+ String message = "";
+ agentLogger.info("createContainer", null, "Creating CGroup Container:" + containerId);
+ String[] command = new String[] { cgroupUtilsDir + "/cgcreate", "-t",
+ userName + ":" + groupName, "-a", userName + ":" + groupName, "-g",
+ cgroupSubsystems + ":" + SYSTEM + "/" + containerId };
+ int retCode = launchCommand(command);
+ // first fetch the location of cgroups on this system. If cgroups is configured
+ // with newer cgconfig add 'memory' to the base dir
+ if (cgroupExists(getCGroupLocation(CGDuccMemoryPath) + containerId)) {
+ // Starting with libcgroup v.0.38, the cgcreate fails
+ // with exit code = 96 even though the cgroup gets
+ // created! The following code treats such return code
+ // as success. In case there is an error, subsequent
+ // cgset or cgexec will fail.
+ if (retCode == 0 || retCode == 96) {
+ containerIds.add(containerId);
+ agentLogger.info("createContainer", null, ">>>>" + "SUCCESS - Created CGroup Container:"
+ + containerId + ". The cgcreate return code:" + retCode);
+ return true;
+ }
+ } else {
+ message = ">>> CGroup Container:" + containerId + " not found in "
+ + getCGroupLocation(CGDuccMemoryPath) + containerId;
+ }
+ agentLogger.error("createContainer", null, message);
+ System.out.println(message);
+ return false;
+ }
+
+ private String composeCpuAccountingFileName(String id) {
+ String location = getCGroupLocation("cpuacct").trim();
+ if (!location.endsWith(System.getProperty("file.separator"))) {
+ location = location + System.getProperty("file.separator");
+ }
+ if (!legacyCgConfig) {
+ location += SYSTEM + System.getProperty("file.separator");
+ }
+ return location + id + "cpuacct.usage";
+ }
+
+ private String composeMemoryStatFileName(String id) {
+ String location = getCGroupLocation("memory").trim();
+ if (!location.endsWith(System.getProperty("file.separator"))) {
+ location = location + System.getProperty("file.separator");
+ }
+ if (!legacyCgConfig) {
+ location += SYSTEM + System.getProperty("file.separator");
+ }
+ return location + id + "memory.stat";
+ }
+
+ public boolean isCpuReportingEnabled() {
+ // String file =
+ // getCGroupLocation("cpuacct")+System.getProperty("file.separator")+"cpuacct.usage";
+
+ File f = new File(composeCpuAccountingFileName(""));
+ System.out.println(f.getAbsolutePath());
+ return f.exists();
+ }
+
+ public long getCpuUsage(String containerId) throws Exception {
+ long usage = 0;
+
+ if (!containerId.endsWith(System.getProperty("file.separator"))) {
+ containerId = containerId + System.getProperty("file.separator");
+ }
+ String file = composeCpuAccountingFileName(containerId.trim());
+ agentLogger.debug("getCpuUsage", null, "CPUACCT.USAGE file:" + file);
+ File f = new File(file);
+ if (f.exists()) {
+ InputStreamReader isr = new InputStreamReader(new FileInputStream(f));
+ BufferedReader br = new BufferedReader(isr);
+ String line;
+ try {
+ while ((line = br.readLine()) != null) {
+ agentLogger.trace("getCpuUsage", null, "CPUACCT.USAGE Line:" + line);
+ usage = Long.parseLong(line.trim());
+ break;
+ }
+ } catch (Exception e) {
+ agentLogger.error("getCpuUsage", null, e);
+ } finally {
+ if (isr != null) {
+ isr.close();
+ }
+ agentLogger.trace("getCpuUsage", null, "Done Reading cpuacct.stat file:" + file);
+
+ }
+ } else {
+ agentLogger.info("getCpuUsage", null,
+ "CPUACCT.USAGE file:" + file + " Not Found - Process CPU Usage is Unavailable");
+
+ usage = -1; // cgroups accounting not configured
+ }
+ return usage;
+
+ }
+
+ public enum CgroupMemoryStat {
+ RSS("rss"), SWAP("swap"), FAULTS("pgpgin");
+
+ String key;
+
+ private CgroupMemoryStat(String aKey) {
+ this.key = aKey;
+ }
+
+ public String getKey() {
+ return this.key;
+ }
+
+ }
+
+ public long getUsageForMemoryStat(CgroupMemoryStat stat, String containerId) throws Exception {
+ long usage = -1;
+
+ if (!containerId.endsWith(System.getProperty("file.separator"))) {
+ containerId = containerId + System.getProperty("file.separator");
+ }
+ String file = composeMemoryStatFileName(containerId.trim());
+ agentLogger.debug("getUsageForMemoryStat", null, "MEMORY.STAT file:" + file);
+ File f = new File(file);
+ if (f.exists()) {
+ InputStreamReader isr = new InputStreamReader(new FileInputStream(f));
+ BufferedReader br = new BufferedReader(isr);
+ String line;
+ try {
+ while ((line = br.readLine()) != null) {
+ agentLogger.debug("getUsageForMemoryStat", null, "MEMORY.STAT Line:" + line);
+ if (line.startsWith(stat.getKey())) {
+ usage = Long.parseLong(line.trim().split(" ")[1]);
+ break;
+ }
+ }
+ } catch (Exception e) {
+ agentLogger.error("getUsageForMemoryStat", null, e);
+ } finally {
+ if (isr != null) {
+ isr.close();
+ }
+ agentLogger.debug("getUsageForMemoryStat", null, "Done Reading memory.stat file:" + file);
+ }
+ } else {
+ agentLogger.debug("getUsageForMemoryStat", null,
+ "MEMORY.STAT file:" + file + " Not Found - Process RSS Usage is Unavailable");
+
+ usage = -1; // cgroups accounting not configured
+ }
+ return usage;
+ }
+
+ /**
+ * Sets the max memory use for an existing cgroup container.
+ *
+ * @param containerId
+ * - existing container id for which limit will be set
+ * @param userId
+ * - container owner
+ * @param useDuccSpawn
+ * - run 'cgset' command as a user
+ * @param containerMaxSize
+ * - max memory limit
+ *
+ * @return - true on success, false otherwise
+ *
+ * @throws Exception
+ */
+
+ public boolean setContainerMaxMemoryLimit(String containerId, String userId, boolean useDuccSpawn,
+ long containerMaxSize) throws Exception {
+ try {
+ String[] command = new String[] { cgroupUtilsDir + "/cgset", "-r",
+ "memory.limit_in_bytes=" + containerMaxSize, SYSTEM + "/" + containerId };
+ int retCode = launchCommand(command);
+ if (retCode == 0) {
+ agentLogger.debug("setContainerMaxMemoryLimit", null,
+ ">>>>" + "SUCCESS - Created CGroup Limit on Container:" + containerId);
+ return true;
+ } else {
+ agentLogger.info("setContainerMaxMemoryLimit", null,
+ ">>>>" + "FAILURE - Unable To Create CGroup Container:" + containerId);
+ return false;
+ }
+ } catch (Exception e) {
+ agentLogger.error("setContainerMaxMemoryLimit", null,
+ ">>>>" + "FAILURE - Unable To Set Limit On CGroup Container:" + containerId, e);
+ return false;
+ }
+ }
+
+ /**
+ * Sets the cpu shares use for an existing cgroup container.
+ *
+ * @param containerId
+ * - existing container id for which limit will be set
+ * @param userId
+ * - container owner
+ * @param useDuccSpawn
+ * - run 'cgset' command as a user
+ * @param containerCpuShares
+ * - cpu shares
+ *
+ * @return - true on success, false otherwise
+ *
+ * @throws Exception
+ */
+
+ public boolean setContainerCpuShares(String containerId, String userId, boolean useDuccSpawn,
+ long containerCpuShares) throws Exception {
+ try {
+ String[] command = new String[] { cgroupUtilsDir + "/cgset", "-r",
+ "cpu.shares=" + containerCpuShares, SYSTEM + "/" + containerId };
+ int retCode = launchCommand(command);
+ if (retCode == 0) {
+ agentLogger.debug("setContainerCpuShares", null,
+ ">>>>" + "SUCCESS - Created CGroup with CPU Shares=" + containerCpuShares
+ + " on Container:" + containerId);
+ return true;
+ } else {
+ agentLogger.info("setContainerCpuShares", null,
+ ">>>>" + "FAILURE - Unable To Set CPU shares on CGroup Container:" + containerId);
+ return false;
+ }
+ } catch (Exception e) {
+ agentLogger.error("setContainerCpuShares", null,
+ ">>>>" + "FAILURE - Unable To Set CPU shares On CGroup Container:" + containerId, e);
+ return false;
+ }
+ }
+
+ /**
+ * Sets the memory swappiness for an existing cgroup container.
+ *
+ * @param containerId
+ * - existing container id for which limit will be set
+ * @param userId
+ * - container owner
+ * @param useDuccSpawn
+ * - run 'cgset' command as a user
+ * @param swappiness
+ * - swappiness
+ *
+ * @return - true on success, false otherwise
+ *
+ * @throws Exception
+ */
+
+ public boolean setContainerSwappiness(String containerId, String userId, boolean useDuccSpawn,
+ long swappiness) throws Exception {
+ try {
[... 507 lines stripped ...]