lxcpp: cgroups API implementation (part 1) 72/49472/10
authorKrzysztof Dynowski <k.dynowski@samsung.com>
Mon, 12 Oct 2015 15:38:27 +0000 (17:38 +0200)
committerJan Olszak <j.olszak@samsung.com>
Thu, 22 Oct 2015 13:27:30 +0000 (06:27 -0700)
[Feature]       Control-groups API for containers
[Cause]         N/A
[Solution]      N/A
[Verification]  build, install, run unit tests

Change-Id: I13e921b7568b9d2adf70c9499360d77daf80dee2

common/utils/fs.cpp
common/utils/text.cpp
common/utils/text.hpp
libs/lxcpp/cgroups/cgroup.cpp
libs/lxcpp/cgroups/cgroup.hpp
libs/lxcpp/cgroups/subsystem.cpp [new file with mode: 0644]
libs/lxcpp/cgroups/subsystem.hpp
libs/lxcpp/exception.hpp
tests/unit_tests/lxcpp/ut-cgroups.cpp [new file with mode: 0644]
tests/unit_tests/lxcpp/ut-network.cpp
tests/unit_tests/utils/ut-text.cpp [new file with mode: 0644]

index 62e6c37..454ea4c 100644 (file)
@@ -220,7 +220,7 @@ bool mount(const std::string& source,
 bool umount(const std::string& path)
 {
     if (::umount(path.c_str()) != 0) {
-        LOGD("Umount failed for '" << path << "': " << getSystemErrorMessage());
+        LOGE("Umount failed for '" << path << "': " << getSystemErrorMessage());
         return false;
     }
     return true;
index 5bdf335..72ba65f 100644 (file)
@@ -22,6 +22,7 @@
  */
 
 #include "utils/text.hpp"
+#include <sstream>
 
 namespace utils {
 namespace {
@@ -40,4 +41,34 @@ std::string toHexString(const void *data, unsigned len)
     return s;
 }
 
+std::string join(const std::vector<std::string>& vec, const char *delim)
+{
+    std::stringstream res;
+    for (const auto& s : vec) {
+        if (res.tellp() > 0) {
+            res << delim;
+        }
+        res << s;
+    }
+    return res.str();
+}
+
+std::vector<std::string> split(const std::string& str, const std::string& delim)
+{
+    std::vector<std::string> tokens;
+    if (str.empty()) {
+        return tokens;
+    }
+
+    for (std::string::size_type startPos = 0; ; ) {
+        std::string::size_type endPos = str.find_first_of(delim, startPos);
+        tokens.push_back(str.substr(startPos, endPos));
+        if (endPos == std::string::npos) {
+            break;
+        }
+        startPos = endPos + 1;
+    }
+    return tokens;
+}
+
 } // namespace utils
index 435f421..465f649 100644 (file)
 #define COMMON_UTILS_TEXT_HPP
 
 #include <string>
+#include <vector>
 
 namespace utils {
 
-/**
- * Convert binary bytes array to hex string representation
- */
-std::string toHexString(const void *data, unsigned len);
-
 inline bool beginsWith(std::string const &value, std::string const &part)
 {
      if (part.size() > value.size()) {
@@ -49,6 +45,14 @@ inline bool endsWith(std::string const &value, std::string const &part)
      return std::equal(part.rbegin(), part.rend(), value.rbegin());
 }
 
+/**
+ * Convert binary bytes array to hex string representation
+ */
+std::string toHexString(const void *data, unsigned len);
+
+std::string join(const std::vector<std::string>& vec, const char *delim);
+std::vector<std::string> split(const std::string& str, const std::string& delim);
+
 } // namespace utils
 
 #endif // COMMON_UTILS_TEXT_HPP
index 574af61..688058d 100644 (file)
  */
 
 #include "lxcpp/cgroups/cgroup.hpp"
+#include "lxcpp/exception.hpp"
+#include "utils/fs.hpp"
+#include "logger/logger.hpp"
 
-// added this file now, to make hpp go through compilation
+namespace fs = boost::filesystem;
+
+namespace lxcpp {
+
+namespace {
+std::string getSubsysName(const std::string& s) {
+    auto p = s.find(':');
+    if (p == std::string::npos) {
+        const std::string msg = "wgrong cgroup format";
+        LOGE(msg);
+        throw CGroupException(msg);
+    }
+    return s.substr(0, p);
+}
+std::string getCGroupName(const std::string& s) {
+    auto p = s.find(':');
+    if (p == std::string::npos) {
+        const std::string msg = "wgrong cgroup format";
+        LOGE(msg);
+        throw CGroupException(msg);
+    }
+    return s.substr(p + 1);
+}
+} // namespace
+
+CGroup::CGroup(const std::string& subsysAndCgroup) :
+    mSubsys(std::move(getSubsysName(subsysAndCgroup))),
+    mName(std::move(getCGroupName(subsysAndCgroup)))
+{
+}
+
+bool CGroup::exists() const
+{
+    const fs::path path = fs::path(mSubsys.getMountPoint()) / mName;
+    return fs::is_directory(path);
+}
+
+void CGroup::create()
+{
+    const fs::path path = fs::path(mSubsys.getMountPoint()) / mName;
+    fs::create_directory(path);
+}
+
+void CGroup::destroy()
+{
+    const fs::path path = fs::path(mSubsys.getMountPoint()) / mName;
+    fs::remove_all(path);
+}
+
+void CGroup::setValue(const std::string& param, const std::string& value)
+{
+    const fs::path path = fs::path(mSubsys.getMountPoint()) / mName / (mSubsys.getName() + "." + param);
+    utils::saveFileContent(path.string(), value);
+}
+
+std::string CGroup::getValue(const std::string& param) const
+{
+    const fs::path path = fs::path(mSubsys.getMountPoint()) / mName / (mSubsys.getName() + "." + param);
+    return utils::readFileContent(path.string());
+}
+
+void CGroup::assignProcess(pid_t pid)
+{
+    const fs::path path = fs::path(mSubsys.getMountPoint()) / mName / "tasks";
+    utils::saveFileContent(path.string(),  std::to_string(pid));
+}
+
+} //namespace lxcpp
index b47268f..f8d5698 100644 (file)
 
 #include "lxcpp/cgroups/subsystem.hpp"
 
+namespace lxcpp {
+
 class CGroup {
 
 public:
     /**
      * Define control-group object
      */
-    CGroup(const Subsystem& subsys, const std::string& name) :
+    CGroup(const std::string& subsys, const std::string& name) :
         mSubsys(subsys),
         mName(name)
     {
     }
 
     /**
+     * Define control-group object (format "subsys:cgroup_path")
+     */
+    CGroup(const std::string& subsysAndCgroup);
+
+    /**
      * Check if cgroup exists
      * @return true if cgroup path (subsys.path / mName) exists
      */
-    bool exists();
+    bool exists() const;
 
     /**
      * Create cgroup directory
@@ -67,17 +74,19 @@ public:
      * Get cgroup parameter
      * Equivalent of: cat mSubsys_path/mName/mSubsys_name.param
      */
-    std::string getValue(const std::string& key);
+    std::string getValue(const std::string& param) const;
 
     /**
-     * Move process to this cgroup (process can't be removed from a cgroup)
+     * Assign process to this cgroup (will be removed from previous cgroup automatically)
      * Equivalent of: echo pid > mSubsys_path/mName/tasks
      */
-    void moveProcess(pid_t pid);
+    void assignProcess(pid_t pid);
 
 private:
-    const Subsystem& mSubsys; // referred subsystem
-    const std::string& mName; // path relative to subsystem "root"
+    const Subsystem mSubsys; // referred subsystem
+    const std::string mName; // path relative to subsystem "root"
 };
 
+} //namespace lxcpp
+
 #endif // LXCPP_CGROUPS_CGROUP_HPP
diff --git a/libs/lxcpp/cgroups/subsystem.cpp b/libs/lxcpp/cgroups/subsystem.cpp
new file mode 100644 (file)
index 0000000..dc18683
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ *  Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License version 2.1 as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * @file
+ * @author  Krzysztof Dynowski (k.dynowski@samsumg.com)
+ * @brief   Control-groups management
+ */
+
+#include "lxcpp/cgroups/subsystem.hpp"
+#include "lxcpp/exception.hpp"
+
+#include "utils/exception.hpp"
+#include "utils/text.hpp"
+#include "utils/fs.hpp"
+#include "logger/logger.hpp"
+
+#include <istream>
+#include <sstream>
+#include <iterator>
+#include <algorithm>
+#include <iostream>
+
+namespace lxcpp {
+
+Subsystem::Subsystem(const std::string& name) : mName(name)
+{
+    if (mName.empty()) {
+        const std::string msg = "CGroup name is empty";
+        LOGE(msg);
+        throw CGroupException(msg);
+    }
+
+    std::vector<std::string> av = availableSubsystems();
+    //find mount point for a name
+    std::ifstream fileStream("/proc/mounts");
+    if (!fileStream.good()) {
+        const std::string msg = "Failed to open /proc/mounts";
+        LOGE(msg);
+        throw CGroupException(msg);
+    }
+
+    std::string line;
+    while (std::getline(fileStream, line).good()) {
+        std::istringstream iss(line);
+        auto it = std::istream_iterator<std::string>(iss);
+        it++; //skip device name (fake for cgroup filesystem type)
+        std::string path = *it++;       //mount point
+        if (it->compare("cgroup") != 0) {    //filesystem type
+            continue;
+        }
+        it++; //skip filesystem type
+        if (it->find(mName) != std::string::npos) {
+            mPath = std::move(path);
+            break;
+        }
+    }
+}
+
+bool Subsystem::isAvailable() const
+{
+    if (mName.empty()) {
+        const std::string msg = "CGroup name is empty";
+        LOGE(msg);
+        throw CGroupException(msg);
+    }
+    std::vector<std::string> av = availableSubsystems();
+    return std::find(av.begin(), av.end(), mName) != av.end();
+}
+
+bool Subsystem::isAttached() const
+{
+    return !mPath.empty();
+}
+
+const std::string& Subsystem::getMountPoint() const
+{
+    if (!isAttached()) {
+        const std::string msg = "CGroup '" + mName + "' is not attached";
+        LOGE(msg);
+        throw CGroupException(msg);
+    }
+    return mPath;
+}
+
+void Subsystem::attach(const std::string& path, const std::vector<std::string>& subs)
+{
+    if (path.empty()) {
+        const std::string msg = "Trying attach to emtpy path";
+        LOGE(msg);
+        throw CGroupException(msg);
+    }
+    if (!utils::createDirs(path,0777)) {
+         throw CGroupException("Can't create mount point: " + path + ", " + utils::getSystemErrorMessage());
+    }
+    if (!utils::mount("cgroup", path, "cgroup", 0, utils::join(subs,","))) {
+         throw CGroupException("Can't mount cgroup: " + path + ", " + utils::getSystemErrorMessage());
+    }
+}
+
+void Subsystem::detach(const std::string& path)
+{
+    if (!utils::umount(path)) {
+         throw CGroupException("Can't umount cgroup: " + path + ", " + utils::getSystemErrorMessage());
+    }
+}
+
+std::vector<std::string> Subsystem::availableSubsystems()
+{
+    std::ifstream fileStream("/proc/cgroups");
+    if (!fileStream.good()) {
+        const std::string msg = "Failed to open /proc/cgroups";
+        LOGE(msg);
+        throw CGroupException(msg);
+    }
+
+    std::vector<std::string> subs;
+    std::string line;
+    while (std::getline(fileStream, line).good()) {
+        if (utils::beginsWith(line, "#")) {
+            continue;
+        }
+        std::istringstream iss(line);
+        auto it = std::istream_iterator<std::string>(iss);
+        std::string n = *it++; //subsystem name
+        subs.push_back(n);
+    }
+    return subs;
+}
+
+std::vector<std::string> Subsystem::getCGroups(pid_t pid)
+{
+    std::ifstream fileStream("/proc/" + std::to_string(pid) + "/cgroup");
+    if (!fileStream.good()) {
+        const std::string msg = "Failed to open /proc/<pid>/cgroup";
+        LOGE(msg);
+        throw CGroupException(msg);
+    }
+
+    std::vector<std::string> subs;
+    std::string line;
+    while (std::getline(fileStream, line).good()) {
+        if (utils::beginsWith(line, "#")) {
+            continue;
+        }
+        // istream_iterator does not support delimiter
+        std::istringstream iss(line);
+        std::string n, p;
+        std::getline(iss, n, ':'); // ignore
+        std::getline(iss, n, ':'); // subsystem name
+        std::getline(iss, p, ':'); // cgroup path
+        subs.push_back(n + ":" + p);
+    }
+    return subs;
+}
+
+} //namespace lxcpp
index bb9887e..a4b7c75 100644 (file)
@@ -29,6 +29,8 @@
 #include <string>
 #include <vector>
 
+namespace lxcpp {
+
 class Subsystem {
 public:
     /**
@@ -36,23 +38,35 @@ public:
      */
     Subsystem(const std::string& name);
 
+    const std::string& getName() const
+    {
+        return mName;
+    }
+
     /**
      * Check if named subsystem is supported by the kernel
      * @return true if subsystem is listed in /proc/cgroups
      */
-    bool isAvailable();
+    bool isAvailable() const;
 
     /**
      * Check if named subsystem is mounted (added to hierarchy)
      * @return true if subsystem has a mount point (as read from /proc/mounts)
      */
-    bool isAttached();
+    bool isAttached() const;
+
+    /**
+     * Get mount point of this subsystem
+     * @return subsystem mount point (as read from /proc/mounts)
+     */
+    const std::string& getMountPoint() const;
 
     /**
      * Attach subsystem hierarchy to filesystem
-     * Equivalent of: mount -t cgroup -o subs(coma-sep) subs(underln-sep) path
+     * Equivalent of: mount -t cgroup -o subs(coma-sep) cgroup path
+     * Note: cgroup root must be already mounted (eg. /sys/fs/cgroup) as tmpfs
      */
-    static void attach(const std::string& path, std::vector<std::string> subs);
+    static void attach(const std::string& path, const std::vector<std::string>& subs);
 
     /**
      * Detach subsstem hierarchy from filesystem
@@ -74,8 +88,10 @@ public:
     static std::vector<std::string> getCGroups(pid_t pid);
 
 private:
-    const std::string& mName;
+    std::string mName;
     std::string mPath;
 };
 
+} //namespace lxcpp
+
 #endif // LXCPP_CGROUPS_SUBSYSTEM_HPP
index 37fec5a..061bc6c 100644 (file)
@@ -91,6 +91,11 @@ struct NetworkException : public Exception {
         : Exception(message) {}
 };
 
+struct CGroupException : public Exception {
+    explicit CGroupException (const std::string& message = "Error during setting up a cgroup")
+        : Exception(message) {}
+};
+
 struct ConfigureException: public Exception {
     explicit ConfigureException(const std::string& message = "Error while configuring a container")
         : Exception(message) {}
diff --git a/tests/unit_tests/lxcpp/ut-cgroups.cpp b/tests/unit_tests/lxcpp/ut-cgroups.cpp
new file mode 100644 (file)
index 0000000..510aba3
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ *  Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License version 2.1 as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * @file
+ * @author  Krzysztof Dynowski (k.dynowski@samsung.com)
+ * @brief   Unit tests of lxcpp cgroups managment
+ */
+
+#include "config.hpp"
+#include "ut.hpp"
+#include "logger/logger.hpp"
+
+#include "lxcpp/cgroups/subsystem.hpp"
+
+namespace {
+
+struct Fixture {
+    Fixture() {}
+    ~Fixture() {}
+};
+
+} // namespace
+
+BOOST_FIXTURE_TEST_SUITE(LxcppCGroupsSuite, Fixture)
+
+using namespace lxcpp;
+
+// assume cgroups are supprted by the system
+BOOST_AUTO_TEST_CASE(GetAvailable)
+{
+    std::vector<std::string> subs;
+    BOOST_CHECK_NO_THROW(subs = Subsystem::availableSubsystems());
+    BOOST_CHECK(subs.size() > 0);
+    for (auto n : subs){
+        Subsystem s(n);
+        LOGD(s.getName() << ": " << (s.isAttached()?s.getMountPoint():"[not attached]"));
+    }
+}
+
+BOOST_AUTO_TEST_CASE(GetCGroupsByPid)
+{
+    std::vector<std::string> cg;
+    BOOST_CHECK_NO_THROW(cg=Subsystem::getCGroups(::getpid()));
+    BOOST_CHECK(cg.size() > 0);
+}
+
+BOOST_AUTO_TEST_CASE(SubsysAttach)
+{
+    Subsystem sub("freezer");
+    BOOST_CHECK_MESSAGE(sub.getName() == "freezer", "freezer not equal");
+    BOOST_CHECK_MESSAGE(sub.isAvailable(), "freezer not found");
+
+    if (!sub.isAvailable()) return ;
+
+
+    if (sub.isAttached()) {
+        std::string mp = sub.getMountPoint();
+        BOOST_CHECK_NO_THROW(Subsystem::detach(mp));
+        BOOST_CHECK_NO_THROW(Subsystem::attach(mp, {sub.getName()}));
+    }
+    else {
+        std::string mp = "/sys/fs/cgroup/" + sub.getName();
+        BOOST_CHECK_NO_THROW(Subsystem::attach(mp, {sub.getName()}));
+        BOOST_CHECK_NO_THROW(Subsystem::detach(mp));
+    }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
index a4741f6..75b94c1 100644 (file)
@@ -30,7 +30,6 @@
 #include "ut.hpp"
 
 #include <iostream>
-#include <sched.h>
 
 #include <net/if.h>
 
diff --git a/tests/unit_tests/utils/ut-text.cpp b/tests/unit_tests/utils/ut-text.cpp
new file mode 100644 (file)
index 0000000..b7d8a6d
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ *  Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License version 2.1 as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * @file
+ * @author  Krzysztof Dynowski (k.dynowski@samsung.com)
+ * @brief   Unit tests of text helpers
+ */
+
+#include "config.hpp"
+#include "ut.hpp"
+
+#include "utils/text.hpp"
+
+BOOST_AUTO_TEST_SUITE(TextUtilsSuite)
+
+BOOST_AUTO_TEST_CASE(SplitText)
+{
+    std::vector<std::string> v;
+    v = utils::split("", ",");
+    BOOST_CHECK(v.size() == 0);
+    v = utils::split("a", ",");
+    BOOST_CHECK(v.size() == 1);
+    v = utils::split(",", ",");
+    BOOST_CHECK(v.size() == 2);
+    v = utils::split("1,2", ",");
+    BOOST_CHECK(v.size() == 2);
+}
+
+BOOST_AUTO_TEST_SUITE_END()