Suspending and resuming domains
authorJan Olszak <j.olszak@samsung.com>
Wed, 5 Mar 2014 09:10:06 +0000 (10:10 +0100)
committerJan Olszak <j.olszak@samsung.com>
Mon, 19 May 2014 11:47:14 +0000 (13:47 +0200)
[Issue#]      PSDAC-64
[Bug]         N/A
[Cause]       N/A
[Solution]    N/A
[Verfication] Build and install, run unit tests.

Change-Id: I6c36ca802835e7e05c21817ef030e73e6e5d4e8d

src/server/include/scs-container.hpp
src/server/src/main.cpp
src/server/src/scs-container.cpp
src/server/unit_tests/ut-scs-container.cpp

index c4dc1f1..be1f928 100644 (file)
@@ -27,6 +27,7 @@
 #define SECURITY_CONTAINERS_SERVER_CONTAINER_HPP
 
 #include "scs-container-config.hpp"
+
 #include <string>
 #include <libvirt/libvirt.h>
 
@@ -35,21 +36,64 @@ namespace security_containers {
 class Container {
 
 public:
-    Container();
+    Container(const std::string& configXML);
     virtual ~Container();
-    void define(const char* configXML = NULL);
-    void undefine();
+
+    /**
+     * Boot the container
+     */
     void start();
+
+    /**
+     * Forcefully stop the container.
+     */
     void stop();
 
+    /**
+     * Gracefully shutdown the domain.
+     * This method will NOT block untill domain is shut down,
+     * because some configurations may ignore this.
+     */
+    void shutdown();
+
+    /**
+     * @return Is the process started?
+     */
+    bool isRunning();
+
+    /**
+     * Check if the container is stopped. It's NOT equivalent to !isRunning,
+     * because it checks different internal libvirt's states. There are other states,
+     * (e.g. paused) when the container isn't runnig nor stopped.
+     *
+     * @return Is the process stopped?
+     */
+    bool isStopped();
+
+    /**
+     * Suspends an active domain, the process is frozen
+     * without further access to CPU resources and I/O,
+     * but the memory used by the domain
+     * at the hypervisor level will stay allocated
+     */
+    void suspend();
+
+    /**
+     * Resume the container after suspension.
+     */
+    void resume();
+
+    /**
+     * @return Is the container in a paused state?
+     */
+    bool isPaused();
+
 private:
     ContainerConfig mConfig;   // container configuration
 
     virConnectPtr mVir = NULL; // pointer to the connection with libvirt
     virDomainPtr  mDom = NULL; // pointer to the domain
 
-    bool mIsRunning = false; // is the domain now running
-
     // TODO: This is a temporary sollution.
     const std::string mDefaultConfigXML = "<domain type=\"lxc\">\
                                          <name>cnsl</name>\
@@ -62,9 +106,14 @@ private:
                                          <console type=\"pty\"/>\
                                          </devices>\
                                          </domain>";
-    void connect();
-    void disconnect();
 
+    void connect();    // fill mVir
+    void disconnect(); // release mVir
+    void define(const std::string& configXML); // containers can't share the same libvirt configuration
+    void undefine();
+
+    int  getState();   // get the libvirt's domain state
+    bool isPMSuspended(); // the cotainer suspended by the power manager
 };
 }
 #endif // SECURITY_CONTAINERS_SERVER_CONTAINER_HPP
index d6cfc8e..a78d768 100644 (file)
  * @brief   Main file for the Security Containers Daemon
  */
 
-#include "scs-container.hpp"  // TODO: Delete
-
 #include <iostream>
 #include <getopt.h>  // For getopt
-#include <unistd.h>  // For sleep
-
-using namespace security_containers;
-
 
 int main(int argc, char* argv[])
 {
index 9be7737..4dff0d8 100644 (file)
 #include "scs-container.hpp"
 #include "scs-exception.hpp"
 #include "scs-log.hpp"
+
 #include <assert.h>
+#include <string>
 
-using namespace security_containers;
+namespace security_containers {
 
-Container::Container()
+Container::Container(const std::string& configXML)
 {
     connect();
+    define(configXML);
 }
 
 
 Container::~Container()
 {
+    // Try to shutdown
+    try {
+        resume();
+        shutdown();
+    } catch (ServerException& e) {}
+
+    // Destroy the container
+    try {
+        stop();
+        undefine();
+    } catch (ServerException& e) {
+        LOGE("Failed to destroy the container!");
+    }
     disconnect();
 }
 
 
-void
-Container::connect()
+void Container::connect()
 {
     assert(mVir == NULL);
 
@@ -54,8 +69,7 @@ Container::connect()
 };
 
 
-void
-Container::disconnect()
+void Container::disconnect()
 {
     if (mVir == NULL) {
         return;
@@ -68,13 +82,12 @@ Container::disconnect()
 };
 
 
-void
-Container::start()
+void Container::start()
 {
     assert(mVir != NULL);
     assert(mDom != NULL);
 
-    if (mIsRunning) {
+    if (isRunning()) {
         return;
     }
 
@@ -87,20 +100,18 @@ Container::start()
         LOGE("Failed to start the container");
         throw DomainOperationException();
     }
-
-    mIsRunning = true;
 };
 
 
-void
-Container::stop()
+void Container::stop()
 {
     assert(mVir != NULL);
     assert(mDom != NULL);
 
-    if (!mIsRunning) {
+    if (!isRunning()) {
         return;
     }
+
     // Forceful termination of the guest
     u_int flags = VIR_DOMAIN_DESTROY_DEFAULT;
 
@@ -108,18 +119,45 @@ Container::stop()
         LOGE("Error during domain stopping");
         throw DomainOperationException();
     }
-
-    mIsRunning = false;
 };
 
 
-void
-Container::define(const char* configXML)
+void Container::shutdown()
+{
+    assert(mVir != NULL);
+    assert(mDom != NULL);
+
+    if (!isRunning()) {
+        return;
+    }
+
+    if (virDomainShutdown(mDom) < 0) {
+        LOGE("Error during domain shutdown");
+        throw DomainOperationException();
+    }
+}
+
+
+bool Container::isRunning()
+{
+    return getState() == VIR_DOMAIN_RUNNING;
+}
+
+
+bool Container::isStopped()
+{
+    int state = getState();
+    return state == VIR_DOMAIN_SHUTDOWN ||
+           state == VIR_DOMAIN_SHUTOFF ||
+           state == VIR_DOMAIN_CRASHED;
+}
+
+void Container::define(const std::string& configXML)
 {
     assert(mVir != NULL);
 
-    if (configXML) {
-        mDom = virDomainDefineXML(mVir, configXML);
+    if (!configXML.empty()) {
+        mDom = virDomainDefineXML(mVir, configXML.c_str());
     } else {
         mDom = virDomainDefineXML(mVir, mDefaultConfigXML.c_str());
     }
@@ -131,8 +169,7 @@ Container::define(const char* configXML)
 };
 
 
-void
-Container::undefine()
+void Container::undefine()
 {
     assert(mVir != NULL);
     assert(mDom != NULL);
@@ -152,3 +189,65 @@ Container::undefine()
 
     mDom = NULL;
 };
+
+
+void Container::suspend()
+{
+    assert(mVir != NULL);
+    assert(mDom != NULL);
+
+    if (isPaused()) {
+        return;
+    }
+
+    if (isPMSuspended() || virDomainSuspend(mDom) < 0) {
+        LOGE("Error during domain suspension");
+        throw DomainOperationException();
+    }
+}
+
+
+void Container::resume()
+{
+    assert(mVir != NULL);
+    assert(mDom != NULL);
+
+    if (!isPaused()) {
+        return;
+    }
+
+    if (isPMSuspended() || virDomainResume(mDom) < 0) {
+        LOGE("Error during domain resumming");
+        throw DomainOperationException();
+    }
+}
+
+
+bool Container::isPaused()
+{
+    return getState() == VIR_DOMAIN_PAUSED;
+}
+
+
+bool Container::isPMSuspended()
+{
+    return getState() == VIR_DOMAIN_PMSUSPENDED;
+}
+
+
+int Container::getState()
+{
+    assert(mVir != NULL);
+    assert(mDom != NULL);
+
+    int state;
+
+    if (virDomainGetState(mDom, &state, NULL, 0)) {
+        LOGE("Error during getting domain's state");
+        throw DomainOperationException();
+    }
+
+    return state;
+}
+
+} // namespace security_containers
\ No newline at end of file
index ce44ee2..a74e9a2 100644 (file)
  */
 
 #include "scs-container.hpp"
+#include "scs-exception.hpp"
+
+#include <memory>
 
 #define BOOST_TEST_DYN_LINK
 #include <boost/test/unit_test.hpp>
 
-using namespace security_containers;
 
 BOOST_AUTO_TEST_SUITE(ContainerSuite)
 
-BOOST_AUTO_TEST_CASE(SimpleTest)
+using namespace security_containers;
+
+const std::string testConfigXML =
+    "<domain type=\"lxc\">                  \
+        <name>cnsl</name>                   \
+        <memory>102400</memory>             \
+        <on_poweroff>destroy</on_poweroff>  \
+        <os>                                \
+            <type>exe</type>                \
+            <init>/bin/sh</init>            \
+        </os>                               \
+        <devices>                           \
+            <console type=\"pty\"/>         \
+        </devices>                          \
+    </domain>";
+
+
+BOOST_AUTO_TEST_CASE(ConstructorTest)
+{
+    BOOST_CHECK_THROW(Container c1("<><TRASHXML>"), ServerException);
+    BOOST_CHECK_NO_THROW(Container c2(testConfigXML));
+}
+
+BOOST_AUTO_TEST_CASE(DestructorTest)
+{
+    std::unique_ptr<Container> c(new Container(testConfigXML));
+    BOOST_REQUIRE_NO_THROW(c.reset());
+}
+
+BOOST_AUTO_TEST_CASE(StartTest)
+{
+    Container c(testConfigXML);
+    BOOST_REQUIRE_NO_THROW(c.start());
+    BOOST_CHECK(c.isRunning());
+}
+
+BOOST_AUTO_TEST_CASE(StopTest)
+{
+    Container c(testConfigXML);
+    BOOST_REQUIRE_NO_THROW(c.start());
+    BOOST_CHECK(c.isRunning());
+    BOOST_REQUIRE_NO_THROW(c.stop())
+    BOOST_CHECK(!c.isRunning());
+    BOOST_CHECK(c.isStopped());
+}
+
+BOOST_AUTO_TEST_CASE(ShutdownTest)
+{
+    Container c(testConfigXML);
+    BOOST_REQUIRE_NO_THROW(c.start())
+    BOOST_CHECK(c.isRunning());
+    BOOST_REQUIRE_NO_THROW(c.shutdown())
+    // TODO: For this simple configuration, the shutdown signal is ignored
+    // BOOST_CHECK(!c.isRunning());
+    // BOOST_CHECK(c.isStopped());
+}
+
+BOOST_AUTO_TEST_CASE(SuspendTest)
+{
+    Container c(testConfigXML);
+    BOOST_REQUIRE_NO_THROW(c.start())
+    BOOST_CHECK(c.isRunning());
+    BOOST_REQUIRE_NO_THROW(c.suspend())
+    BOOST_CHECK(!c.isRunning());
+    BOOST_CHECK(c.isPaused());
+}
+
+BOOST_AUTO_TEST_CASE(ResumeTest)
 {
-    Container c;
-    c.define();
-    c.start();
-    c.stop();
-    c.undefine();
+    Container c(testConfigXML);
+    BOOST_REQUIRE_NO_THROW(c.start());
+    BOOST_REQUIRE_NO_THROW(c.suspend())
+    BOOST_CHECK(c.isPaused());
+    BOOST_REQUIRE_NO_THROW(c.resume());
+    BOOST_CHECK(!c.isPaused());
+    BOOST_CHECK(c.isRunning());
 }
 
 BOOST_AUTO_TEST_SUITE_END()