Lxc server configs, missing ContainerAdmin stuff 92/29892/3
authorPiotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
Wed, 5 Nov 2014 14:00:03 +0000 (15:00 +0100)
committerPiotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
Wed, 12 Nov 2014 12:31:32 +0000 (13:31 +0100)
WORK IN PROGRESS

[Bug/Feature]   N/A
[Cause]         N/A
[Solution]      N/A
[Verification]  All tests should pass

Change-Id: I87505823a5b1c543ee495ede010430bb8c745736

19 files changed:
common/lxc/domain.cpp
common/lxc/domain.hpp
packaging/security-containers.spec
server/configs/CMakeLists.txt
server/configs/containers/business.conf
server/configs/containers/private.conf
server/configs/daemon.conf
server/configs/lxc-templates/business.sh [new file with mode: 0755]
server/configs/lxc-templates/private.sh [new file with mode: 0755]
server/container-admin.cpp
server/container-admin.hpp
tests/unit_tests/lxc/ut-domain.cpp
tests/unit_tests/server/configs/CMakeLists.txt
tests/unit_tests/server/configs/ut-container-admin/containers/buggy.conf [moved from tests/unit_tests/server/configs/ut-container-admin/containers/buggy.conf.in with 71% similarity]
tests/unit_tests/server/configs/ut-container-admin/containers/missing.conf
tests/unit_tests/server/configs/ut-container-admin/containers/test-no-shutdown.conf [moved from tests/unit_tests/server/configs/ut-container-admin/containers/test-no-shutdown.conf.in with 71% similarity]
tests/unit_tests/server/configs/ut-container-admin/containers/test.conf [moved from tests/unit_tests/server/configs/ut-container-admin/containers/test.conf.in with 65% similarity]
tests/unit_tests/server/ut-container-admin.cpp
tests/unit_tests/server/ut-container.cpp

index 07b7c77..0959959 100644 (file)
 #include <lxc/lxccontainer.h>
 #include <sys/stat.h>
 
+#include <map>
+
 namespace security_containers {
 namespace lxc {
 
+namespace {
+    const std::map<std::string, LxcDomain::State> STATE_MAP = {
+        {"STOPPED", LxcDomain::State::STOPPED},
+        {"STARTING", LxcDomain::State::STARTING},
+        {"RUNNING", LxcDomain::State::RUNNING},
+        {"STOPPING", LxcDomain::State::STOPPING},
+        {"ABORTING", LxcDomain::State::ABORTING},
+        {"FREEZING", LxcDomain::State::FREEZING},
+        {"FROZEN", LxcDomain::State::FROZEN},
+        {"THAWED", LxcDomain::State::THAWED}
+    };
+} // namespace
 
 LxcDomain::LxcDomain(const std::string& lxcPath, const std::string& domainName)
   : mContainer(nullptr)
@@ -70,63 +84,92 @@ bool LxcDomain::isDefined()
     return mContainer->is_defined(mContainer);
 }
 
-bool LxcDomain::isRunning()
-{
-    return mContainer->is_running(mContainer);
-}
-
-std::string LxcDomain::getState()
+LxcDomain::State LxcDomain::getState()
 {
-    return mContainer->state(mContainer);
+    const std::string str = mContainer->state(mContainer);
+    return STATE_MAP.at(str);
 }
 
-void LxcDomain::create(const std::string& templatePath)
+bool LxcDomain::create(const std::string& templatePath)
 {
     if (!mContainer->create(mContainer, templatePath.c_str(), NULL, NULL, 0, NULL)) {
         LOGE("Could not create domain " + getName());
-        throw LxcException("Could not create domain");
+        return false;
     }
+    return true;
 }
 
-void LxcDomain::destroy()
+bool LxcDomain::destroy()
 {
     if (!mContainer->destroy(mContainer)) {
         LOGE("Could not destroy domain " + getName());
-        throw LxcException("Could not destroy domain");
+        return false;
     }
+    return true;
 }
 
-void LxcDomain::start(const char* const* argv)
+bool LxcDomain::start(const char* const* argv)
 {
+    if (mContainer->is_running(mContainer)) {
+        LOGE("Already started " + getName());
+        return false;
+    }
+    if (!mContainer->want_daemonize(mContainer, true)) {
+        LOGE("Could not configure domain " + getName());
+        return false;
+    }
     if (!mContainer->start(mContainer, false, const_cast<char* const*>(argv))) {
         LOGE("Could not start domain " + getName());
-        throw LxcException("Could not start domain");
+        return false;
     }
+    return true;
 }
 
-void LxcDomain::stop()
+bool LxcDomain::stop()
 {
     if (!mContainer->stop(mContainer)) {
         LOGE("Could not stop domain " + getName());
-        throw LxcException("Stop domain failed");
+        return false;
     }
+    return true;
 }
 
-void LxcDomain::reboot()
+bool LxcDomain::reboot()
 {
     if (!mContainer->reboot(mContainer)) {
         LOGE("Could not reboot domain " + getName());
-        throw LxcException("Reboot domain failed");
+        return false;
     }
+    return true;
 }
 
-void LxcDomain::shutdown(int timeout)
+bool LxcDomain::shutdown(int timeout)
 {
     if (!mContainer->shutdown(mContainer, timeout)) {
         LOGE("Could not gracefully shutdown domain " + getName() + " in " << timeout << "s");
-        throw LxcException("Shutdown domain failed");
+        return false;
     }
+    return true;
 }
 
+bool LxcDomain::freeze()
+{
+    if (!mContainer->freeze(mContainer)) {
+        LOGE("Could not freeze domain " + getName());
+        return false;
+    }
+    return true;
+}
+
+bool LxcDomain::unfreeze()
+{
+    if (!mContainer->unfreeze(mContainer)) {
+        LOGE("Could not unfreeze domain " + getName());
+        return false;
+    }
+    return true;
+}
+
+
 } // namespace lxc
 } // namespace security_containers
index 2f0cbf8..3202f54 100644 (file)
@@ -39,6 +39,17 @@ namespace lxc {
  */
 class LxcDomain {
 public:
+    enum class State {
+        STOPPED,
+        STARTING,
+        RUNNING,
+        STOPPING,
+        ABORTING,
+        FREEZING,
+        FROZEN,
+        THAWED
+    };
+
     LxcDomain(const std::string& lxcPath, const std::string& domainName);
     ~LxcDomain();
 
@@ -50,17 +61,19 @@ public:
     std::string getConfigItem(const std::string& key);
 
     bool isDefined();
-    bool isRunning();
 
-    std::string getState();
+    State getState();
+
+    bool create(const std::string& templatePath);
+    bool destroy();
 
-    void create(const std::string& templatePath);
-    void destroy();
+    bool start(const char* const* argv);
+    bool stop();
+    bool reboot();
+    bool shutdown(int timeout);
 
-    void start(const char* const* argv);
-    void stop();
-    void reboot();
-    void shutdown(int timeout);
+    bool freeze();
+    bool unfreeze();
 private:
     lxc_container* mContainer;
 };
index e73694c..11e7c99 100644 (file)
@@ -40,8 +40,10 @@ between them. A process from inside a container can request a switch of context
 %attr(755,root,root) %{_bindir}/security-containers-server
 %dir /etc/security-containers
 %dir /etc/security-containers/containers
+%dir /etc/security-containers/lxc-templates
 %config /etc/security-containers/daemon.conf
 %config /etc/security-containers/containers/*.conf
+%attr(755,root,root) /etc/security-containers/lxc-templates/*.sh
 %{_unitdir}/security-containers.service
 %{_unitdir}/multi-user.target.wants/security-containers.service
 /etc/dbus-1/system.d/org.tizen.containers.host.conf
index 310f407..ab4a94f 100644 (file)
@@ -20,6 +20,7 @@
 MESSAGE(STATUS "Installing configs to " ${SC_CONFIG_INSTALL_DIR})
 
 FILE(GLOB container_CONF     containers/*.conf)
+FILE(GLOB admin_CONF         lxc-templates/*.sh)
 
 ## Generate ####################################################################
 CONFIGURE_FILE(systemd/security-containers.service.in
@@ -40,5 +41,8 @@ INSTALL(FILES       ${CMAKE_BINARY_DIR}/dbus-1/system.d/org.tizen.containers.hos
 INSTALL(FILES       ${container_CONF}
         DESTINATION ${SC_CONFIG_INSTALL_DIR}/containers)
 
+INSTALL(PROGRAMS    ${admin_CONF}
+        DESTINATION ${SC_CONFIG_INSTALL_DIR}/lxc-templates)
+
 INSTALL(FILES       ${CMAKE_BINARY_DIR}/systemd/security-containers.service
         DESTINATION ${SYSTEMD_UNIT_DIR})
index c141111..92eb06d 100644 (file)
@@ -1,6 +1,6 @@
 {
     "name" : "business",
-    "lxcTemplate" : "",
+    "lxcTemplate" : "business.sh",
     "initWithArgs" : [],
     "cpuQuotaForeground" : -1,
     "cpuQuotaBackground" : 10000,
index 94261b8..074f4f3 100644 (file)
@@ -1,6 +1,6 @@
 {
     "name" : "private",
-    "lxcTemplate" : "",
+    "lxcTemplate" : "private.sh",
     "initWithArgs" : [],
     "cpuQuotaForeground" : -1,
     "cpuQuotaBackground" : 10000,
index 116b32e..058a3cd 100644 (file)
@@ -7,8 +7,8 @@
     "runMountPointPrefix" : "/var/run/containers",
     "foregroundId" : "private",
     "defaultId" : "private",
-    "lxcTemplatePrefix" : "TODO",
-    "inputConfig" : {"enabled" : true,
+    "lxcTemplatePrefix" : "/etc/security-containers/lxc-templates",
+    "inputConfig" : {"enabled" : false,
                      "device" : "gpio_keys.6",
                      "code" : 139,
                      "numberOfEvents" : 1,
diff --git a/server/configs/lxc-templates/business.sh b/server/configs/lxc-templates/business.sh
new file mode 100755 (executable)
index 0000000..75be9e6
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+echo LXC template, args: $@
+
+options=$(getopt -o p:n: -l rootfs:,path:,name: -- "$@")
+if [ $? -ne 0 ]; then
+    exit 1
+fi
+eval set -- "$options"
+
+while true
+do
+    case "$1" in
+        -p|--path)      path=$2; shift 2;;
+        --rootfs)       rootfs=$2; shift 2;;
+        -n|--name)      name=$2; shift 2;;
+        --)             shift 1; break ;;
+        *)              break ;;
+    esac
+done
+
+# XXX assume rootfs if mounted from iso
+
+# Prepare container configuration file
+> ${path}/config
+cat <<EOF >> ${path}/config
+lxc.utsname = ${name}
+lxc.rootfs = ${rootfs}
+
+lxc.haltsignal = SIGTERM
+
+lxc.pts = 256
+lxc.tty = 0
+
+lxc.mount.auto = proc sys cgroup
+lxc.mount.entry = /var/run/containers/business/run var/run none rw,bind 0 0
+EOF
+
diff --git a/server/configs/lxc-templates/private.sh b/server/configs/lxc-templates/private.sh
new file mode 100755 (executable)
index 0000000..2926e55
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+echo LXC template, args: $@
+
+options=$(getopt -o p:n: -l rootfs:,path:,name: -- "$@")
+if [ $? -ne 0 ]; then
+    exit 1
+fi
+eval set -- "$options"
+
+while true
+do
+    case "$1" in
+        -p|--path)      path=$2; shift 2;;
+        --rootfs)       rootfs=$2; shift 2;;
+        -n|--name)      name=$2; shift 2;;
+        --)             shift 1; break ;;
+        *)              break ;;
+    esac
+done
+
+# XXX assume rootfs if mounted from iso
+
+# Prepare container configuration file
+> ${path}/config
+cat <<EOF >> ${path}/config
+lxc.utsname = ${name}
+lxc.rootfs = ${rootfs}
+
+lxc.haltsignal = SIGTERM
+
+lxc.pts = 256
+lxc.tty = 0
+
+lxc.mount.auto = proc sys cgroup
+lxc.mount.entry = /var/run/containers/private/run var/run none rw,bind 0 0
+EOF
+
index 11c88fd..71185db 100644 (file)
 #include "exception.hpp"
 
 #include "logger/logger.hpp"
-#include "utils/fs.hpp"
 #include "utils/paths.hpp"
-#include "utils/latch.hpp"
-#include "utils/callback-wrapper.hpp"
 
 #include <cassert>
-#include <cstring>
-#include <string>
-#include <memory>
-#include <cstdint>
 
 
 namespace security_containers {
@@ -45,7 +38,7 @@ namespace security_containers {
 namespace {
 
 // TODO: this should be in container's configuration file
-const int SHUTDOWN_WAIT = 10 * 1000;
+const int SHUTDOWN_WAIT = 10;
 
 class Args {
 public:
@@ -89,50 +82,19 @@ ContainerAdmin::ContainerAdmin(const std::string& containersPath,
     : mConfig(config),
       mDom(containersPath, config.name),
       mId(mDom.getName()),
-      mDetachOnExit(false),
-      mLifecycleCallbackId(-1),
-      mRebootCallbackId(-1),
-      mNextIdForListener(1)
+      mDetachOnExit(false)
 {
     LOGD(mId << ": Instantiating ContainerAdmin object");
 
     if (!mDom.isDefined()) {
 
-        std::string lxcTemplate = utils::getAbsolutePath(config.lxcTemplate, lxcTemplatePrefix);
+        const std::string lxcTemplate = utils::getAbsolutePath(config.lxcTemplate,
+                                                               lxcTemplatePrefix);
         LOGI(mId << ": Creating domain from template: " << lxcTemplate);
-        mDom.create(lxcTemplate);
+        if (!mDom.create(lxcTemplate)) {
+            throw ContainerOperationException("Could not create domain");
+        }
     }
-
-//    // ContainerAdmin owns those callbacks
-//    mLifecycleCallbackId = virConnectDomainEventRegisterAny(virDomainGetConnect(mDom.get()),
-//                                                            mDom.get(),
-//                                                            VIR_DOMAIN_EVENT_ID_LIFECYCLE,
-//                                                            VIR_DOMAIN_EVENT_CALLBACK(&ContainerAdmin::libvirtLifecycleCallback),
-//                                                            utils::createCallbackWrapper(this, mLibvirtGuard.spawn()),
-//                                                            &utils::deleteCallbackWrapper<ContainerAdmin*>);
-//
-//    if (mLifecycleCallbackId < 0) {
-//        LOGE(mId << ": Failed to register a libvirt lifecycle callback");
-//        throw ContainerOperationException(mId + ": Failed to register a libvirt lifecycle callback");
-//    }
-//
-//    LOGT(mId << ": registered lifecycle callback");
-//
-//    mRebootCallbackId = virConnectDomainEventRegisterAny(virDomainGetConnect(mDom.get()),
-//                                                         mDom.get(),
-//                                                         VIR_DOMAIN_EVENT_ID_REBOOT,
-//                                                         VIR_DOMAIN_EVENT_CALLBACK(&ContainerAdmin::libvirtRebootCallback),
-//                                                         utils::createCallbackWrapper(this, mLibvirtGuard.spawn()),
-//                                                         &utils::deleteCallbackWrapper<ContainerAdmin*>);
-//
-//    if (mRebootCallbackId < 0) {
-//        LOGE(mId << ": Failed to register a libvirt reboot callback");
-//        virConnectDomainEventDeregisterAny(virDomainGetConnect(mDom.get()),
-//                                           mLifecycleCallbackId);
-//        throw ContainerOperationException(mId + ": Failed to register a libvirt reboot callback");
-//    }
-//
-//    LOGT(mId << ": registered reboot callback");
 }
 
 
@@ -140,22 +102,10 @@ ContainerAdmin::~ContainerAdmin()
 {
     LOGD(mId << ": Destroying ContainerAdmin object...");
 
-//    // Deregister callbacks
-//    if (mLifecycleCallbackId >= 0) {
-//        virConnectDomainEventDeregisterAny(virDomainGetConnect(mDom.get()),
-//                                           mLifecycleCallbackId);
-//    }
-//    if (mRebootCallbackId >= 0) {
-//        virConnectDomainEventDeregisterAny(virDomainGetConnect(mDom.get()),
-//                                           mRebootCallbackId);
-//    }
-//
-    // Try to forcefully stop
     if (!mDetachOnExit) {
-        try {
-            destroy();
-        } catch (ServerException&) {
-            LOGE(mId << ": Failed to destroy the container");
+        // Try to forcefully stop
+        if (!mDom.stop()) {
+            LOGE(mId << ": Failed to stop the container");
         }
     }
 
@@ -177,25 +127,19 @@ void ContainerAdmin::start()
         return;
     }
 
-    Args args(mConfig.initWithArgs);
+    const Args args(mConfig.initWithArgs);
+    bool result;
     if (args.empty()) {
-        mDom.start(NULL);
+        result = mDom.start(NULL);
     } else {
         LOGD(mId << ": Init: " << args);
-        mDom.start(args.getAsCArray());
+        result = mDom.start(args.getAsCArray());
+    }
+
+    if (!result) {
+        throw ContainerOperationException("Could not start container");
     }
 
-//    // In order to update daemon without shutting down the containers
-//    // autodestroy option must NOT be set. It's best to create domain
-//    // without any flags.
-//    u_int flags = VIR_DOMAIN_NONE;
-//
-//    if (virDomainCreateWithFlags(mDom.get(), flags) < 0) {
-//        LOGE(mId << ": Failed to start the container\n"
-//             << libvirt::libvirtFormatError());
-//        throw ContainerOperationException();
-//    }
-//
     LOGD(mId << ": Started");
 }
 
@@ -208,154 +152,64 @@ void ContainerAdmin::stop()
         return;
     }
 
-    mDom.stop();
+    if (!mDom.shutdown(SHUTDOWN_WAIT)) {
+        // force stop
+        if (!mDom.stop()) {
+            throw ContainerOperationException("Could not stop container");
+        }
+    }
 
-//    utils::Latch stoppedOccured;
-//
-//    LifecycleListener setStopped = [&](const int eventId, const int detailId) {
-//        if (eventId == VIR_DOMAIN_EVENT_STOPPED) {
-//            if (detailId != VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN) {
-//                LOGW(mId << ": shutdown requested, but the container stopped with a different status: "
-//                     << libvirt::libvirtEventDetailToString(eventId, detailId));
-//            }
-//            stoppedOccured.set();
-//        }
-//    };
-//
-//    ListenerId id = registerLifecycleListener(setStopped, nullptr);
-//    shutdown();
-//    bool stopped = stoppedOccured.wait(SHUTDOWN_WAIT);
-//    removeListener(id);
-//
-//    if (!stopped) {
-//        LOGW(mId << ": Gracefull shutdown timed out, the container is still running, destroying");
-//        destroy();
-//    }
-//
     LOGD(mId << ": Stopping procedure ended");
 }
 
 
 void ContainerAdmin::destroy()
 {
-    LOGD(mId << ": Destroying...");
-    if (isStopped()) {
-        LOGD(mId << ": Already crashed/down/off - nothing to do");
-        return;
-    }
-
-    mDom.stop();//TODO
-
-//    setSchedulerLevel(SchedulerLevel::FOREGROUND);
-//
-//    // Forceful termination of the guest
-//    u_int flags = VIR_DOMAIN_DESTROY_DEFAULT;
-//
-//    if (virDomainDestroyFlags(mDom.get(), flags) < 0) {
-//        LOGE(mId << ": Error while destroying the container:\n"
-//             << libvirt::libvirtFormatError());
-//        throw ContainerOperationException();
-//    }
-//
-    LOGD(mId << ": Destroyed");
-}
+    LOGD(mId << ": Destroying procedure started...");
 
-
-void ContainerAdmin::shutdown()
-{
-    LOGD(mId << ": Shutting down...");
-    if (isStopped()) {
-        LOGD(mId << ": Already crashed/down/off - nothing to do");
-        return;
+    if (!mDom.destroy()) {
+        throw ContainerOperationException("Could not destroy container");
     }
 
-    mDom.stop(); //TODO
-
-//    setSchedulerLevel(SchedulerLevel::FOREGROUND);
-//
-//    if (virDomainShutdownFlags(mDom.get(), VIR_DOMAIN_SHUTDOWN_SIGNAL) < 0) {
-//        LOGE(mId << ": Error while shutting down the container:\n"
-//             << libvirt::libvirtFormatError());
-//        throw ContainerOperationException();
-//    }
-//
-    LOGD(mId << ": Shut down initiated (async)");
+    LOGD(mId << ": Destroying procedure ended");
 }
 
 
 bool ContainerAdmin::isRunning()
 {
-    return mDom.isRunning();
+    return mDom.getState() == lxc::LxcDomain::State::RUNNING;
 }
 
 
 bool ContainerAdmin::isStopped()
 {
-    return !mDom.isRunning();//TODO
+    return mDom.getState() == lxc::LxcDomain::State::STOPPED;
 }
 
 
 void ContainerAdmin::suspend()
 {
-//    assert(mDom);
-//
-//    LOGD(mId << ": Pausing...");
-//    if (isPaused()) {
-//        LOGD(mId << ": Already paused - nothing to do...");
-//        return;
-//    }
-//
-//    if (virDomainSuspend(mDom.get()) < 0) {
-//        LOGE(mId << ": Error while suspending the container:\n"
-//             << libvirt::libvirtFormatError());
-//        throw ContainerOperationException();
-//    }
-//
-//    LOGD(mId << ": Paused");
+    LOGD(mId << ": Pausing...");
+    if (!mDom.freeze()) {
+        throw ContainerOperationException("Could not pause container");
+    }
+    LOGD(mId << ": Paused");
 }
 
 
 void ContainerAdmin::resume()
 {
-//    assert(mDom);
-//
-//    LOGD(mId << ": Resuming...");
-//    if (!isPaused()) {
-//        LOGD(mId << ": Is not paused - nothing to do...");
-//        return;
-//    }
-//
-//    if (virDomainResume(mDom.get()) < 0) {
-//        LOGE(mId << ": Error while resuming the container:\n"
-//             << libvirt::libvirtFormatError());
-//        throw ContainerOperationException();
-//    }
-//
-//    LOGD(mId << ": Resumed");
+    LOGD(mId << ": Resuming...");
+    if (!mDom.unfreeze()) {
+        throw ContainerOperationException("Could not resume container");
+    }
+    LOGD(mId << ": Resumed");
 }
 
 
 bool ContainerAdmin::isPaused()
 {
-//    return getState() == VIR_DOMAIN_PAUSED;
-    return false;//TODO
-}
-
-
-int ContainerAdmin::getState()
-{
-//    assert(mDom);
-//
-//    int state;
-//
-//    if (virDomainGetState(mDom.get(), &state, NULL, 0)) {
-//        LOGE(mId << ": Error while getting the container's state:\n"
-//             << libvirt::libvirtFormatError());
-//        throw ContainerOperationException();
-//    }
-//
-//    return state;
-    return 0;
+    return mDom.getState() == lxc::LxcDomain::State::FROZEN;
 }
 
 
@@ -443,77 +297,5 @@ std::int64_t ContainerAdmin::getSchedulerQuota()
     return 0;
 }
 
-ContainerAdmin::ListenerId ContainerAdmin::registerLifecycleListener(const ContainerAdmin::LifecycleListener& listener,
-                                                                     const utils::CallbackGuard::Tracker& tracker)
-{
-
-    utils::CallbackWrapper<LifecycleListener> wrap(listener, tracker);
-
-    std::unique_lock<std::mutex> lock(mListenerMutex);
-    unsigned int id = mNextIdForListener++;
-    mLifecycleListeners.insert(LifecycleListenerMap::value_type(id, std::move(wrap)));
-
-    return id;
-}
-
-ContainerAdmin::ListenerId ContainerAdmin::registerRebootListener(const ContainerAdmin::RebootListener& listener,
-                                                                  const utils::CallbackGuard::Tracker& tracker)
-{
-
-    utils::CallbackWrapper<RebootListener> wrap(listener, tracker);
-
-    std::unique_lock<std::mutex> lock(mListenerMutex);
-    unsigned int id = mNextIdForListener++;
-    mRebootListeners.insert(RebootListenerMap::value_type(id, std::move(wrap)));
-
-    return id;
-}
-
-void ContainerAdmin::removeListener(const ContainerAdmin::ListenerId id)
-{
-    std::unique_lock<std::mutex> lock(mListenerMutex);
-    mLifecycleListeners.erase(id);
-    mRebootListeners.erase(id);
-}
-
-//int ContainerAdmin::libvirtLifecycleCallback(virConnectPtr /*con*/,
-//                                             virDomainPtr /*dom*/,
-//                                             int event,
-//                                             int detail,
-//                                             void* opaque)
-//{
-//    ContainerAdmin* thisPtr = utils::getCallbackFromPointer<ContainerAdmin*>(opaque);
-//
-//    LOGI(thisPtr->getId()
-//         << ": Lifecycle event: "
-//         << libvirt::libvirtEventToString(event)
-//         << ": "
-//         << libvirt::libvirtEventDetailToString(event, detail));
-//
-//    std::unique_lock<std::mutex> lock(thisPtr->mListenerMutex);
-//    for (auto& it : thisPtr->mLifecycleListeners) {
-//        LifecycleListener f = it.second.get();
-//        f(event, detail);
-//    }
-//
-//    // ignored, libvirt's legacy
-//    return 0;
-//}
-//
-//void ContainerAdmin::libvirtRebootCallback(virConnectPtr /*con*/,
-//                                           virDomainPtr /*dom*/,
-//                                           void* opaque)
-//{
-//    ContainerAdmin* thisPtr = utils::getCallbackFromPointer<ContainerAdmin*>(opaque);
-//
-//    LOGI(thisPtr->getId() << ": Reboot event");
-//
-//    std::unique_lock<std::mutex> lock(thisPtr->mListenerMutex);
-//    for (auto& it : thisPtr->mRebootListeners) {
-//        RebootListener f = it.second.get();
-//        f();
-//    }
-//}
-
 
 } // namespace security_containers
index f2ca5f9..79b0193 100644 (file)
 #define SERVER_CONTAINER_ADMIN_HPP
 
 #include "container-config.hpp"
-
-#include "utils/callback-guard.hpp"
-#include "utils/callback-wrapper.hpp"
 #include "lxc/domain.hpp"
 
-#include <map>
-#include <mutex>
-#include <string>
-#include <cstdint>
-
 
 namespace security_containers {
 
@@ -49,25 +41,6 @@ enum class SchedulerLevel {
 class ContainerAdmin {
 
 public:
-    /**
-     * A listener ID type.
-     */
-    typedef unsigned int ListenerId;
-
-    /**
-     * An invalid ListenerId value.
-     */
-    static const ListenerId LISTENER_ID_INVALID = 0;
-
-    /**
-     * A function type used for the lifecycle listener
-     */
-    typedef std::function<void(const int eventId, const int detailId)> LifecycleListener;
-
-    /**
-     * A function type used for the reboot listener
-     */
-    typedef std::function<void()> RebootListener;
 
     /**
      * ContainerAdmin constructor
@@ -91,29 +64,23 @@ public:
     void start();
 
     /**
-     * Try to shutdown the container, if failed, destroy it.
+     * Try to shutdown the container, if failed, kill it.
      */
     void stop();
 
     /**
-     * Forcefully stop the container.
+     * Destroy stopped container. In particular it removes whole containers rootfs.
      */
     void destroy();
 
     /**
-     * Gracefully shutdown the container.
-     * This method will NOT block until container is shut down.
-     */
-    void shutdown();
-
-    /**
      * @return Is the container running?
      */
     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,
+     * because it checks different internal lxc states. There are other states,
      * (e.g. paused) when the container isn't running nor stopped.
      *
      * @return Is the container stopped?
@@ -154,66 +121,13 @@ public:
      */
     std::int64_t getSchedulerQuota();
 
-    /**
-     * Sets a listener for a lifecycle event.
-     * It's a caller's responsibility to remove the listener
-     * prior to destroying the object.
-     *
-     * @return listener ID that can be used to remove.
-     */
-    ListenerId registerLifecycleListener(const LifecycleListener& listener,
-                                         const utils::CallbackGuard::Tracker& tracker);
-
-    /**
-     * Sets a listener for a reboot event.
-     * It's a caller's responsibility to remove the listener
-     * prior to destroying the object.
-     *
-     * @return listener ID that can be used to remove.
-     */
-    ListenerId registerRebootListener(const RebootListener& listener,
-                                      const utils::CallbackGuard::Tracker& tracker);
-
-    /**
-     * Remove a previously registered listener.
-     */
-    void removeListener(const ListenerId id);
-
 private:
     const ContainerConfig& mConfig;
     lxc::LxcDomain mDom;
     const std::string mId;
     bool mDetachOnExit;
 
-    int getState();   // get the libvirt's domain state
     void setSchedulerParams(std::uint64_t cpuShares, std::uint64_t vcpuPeriod, std::int64_t vcpuQuota);
-
-    // for handling libvirt callbacks
-    utils::CallbackGuard mLibvirtGuard;
-    int mLifecycleCallbackId;
-    int mRebootCallbackId;
-
-//    // virConnectDomainEventCallback
-//    static int libvirtLifecycleCallback(virConnectPtr con,
-//                                        virDomainPtr dom,
-//                                        int event,
-//                                        int detail,
-//                                        void* opaque);
-//
-//    // virConnectDomainEventGenericCallback
-//    static void libvirtRebootCallback(virConnectPtr con,
-//                                      virDomainPtr dom,
-//                                      void* opaque);
-
-    // for handling external listeners triggered from libvirt callbacks
-    // TODO, the Listener type might not be unique, reimplement using proper listeners
-    typedef std::map<ListenerId, utils::CallbackWrapper<LifecycleListener>> LifecycleListenerMap;
-    typedef std::map<ListenerId, utils::CallbackWrapper<RebootListener>> RebootListenerMap;
-
-    std::mutex mListenerMutex;
-    unsigned int mNextIdForListener;
-    LifecycleListenerMap mLifecycleListeners;
-    RebootListenerMap mRebootListeners;
 };
 
 
index b3b693a..206d2c7 100644 (file)
@@ -60,12 +60,18 @@ struct Fixture {
     {
         LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
         if (lxc.isDefined()) {
-            if (lxc.isRunning()) {
+            if (lxc.getState() != LxcDomain::State::STOPPED) {
                 lxc.stop();
             }
             lxc.destroy();
         }
     }
+
+    void waitForInit()
+    {
+        // wait for init fully started (wait for bash to be able to trap SIGTERM)
+        std::this_thread::sleep_for(std::chrono::milliseconds(200));
+    }
 };
 
 } // namespace
@@ -82,13 +88,13 @@ BOOST_AUTO_TEST_CASE(CreateDestroyTest)
     LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
     BOOST_CHECK(!lxc.isDefined());
 
-    lxc.create(TEMPLATE);
+    BOOST_CHECK(lxc.create(TEMPLATE));
 
     BOOST_CHECK(lxc.isDefined());
     BOOST_CHECK_EQUAL(lxc.getConfigItem("lxc.rootfs"), LXC_PATH + DOMAIN_NAME + "/rootfs");
     BOOST_CHECK_THROW(lxc.getConfigItem("xxx"), LxcException);
 
-    lxc.destroy();
+    BOOST_CHECK(lxc.destroy());
 
     BOOST_CHECK(!lxc.isDefined());
 }
@@ -97,46 +103,146 @@ BOOST_AUTO_TEST_CASE(StartShutdownTest)
 {
     {
         LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
-        lxc.create(TEMPLATE);
+        BOOST_CHECK(lxc.create(TEMPLATE));
     }
     LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
-    BOOST_CHECK_EQUAL("STOPPED", lxc.getState());
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED);
     const char* argv[] = {
         "/bin/sh",
         "-c",
         "trap exit SIGTERM; read",
         NULL
     };
-    lxc.start(argv);
-    // wait for bash to be able to trap SIGTERM
-    std::this_thread::sleep_for(std::chrono::milliseconds(200));
-    BOOST_CHECK_EQUAL("RUNNING", lxc.getState());
-    lxc.shutdown(2);
-    BOOST_CHECK_EQUAL("STOPPED", lxc.getState());
-
-    lxc.destroy();
+    BOOST_CHECK(lxc.start(argv));
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::RUNNING);
+    waitForInit();
+    BOOST_CHECK(lxc.shutdown(2));
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED);
+
+    BOOST_CHECK(lxc.destroy());
 }
 
 BOOST_AUTO_TEST_CASE(StartStopTest)
 {
     {
         LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
-        lxc.create(TEMPLATE);
+        BOOST_CHECK(lxc.create(TEMPLATE));
+    }
+    LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED);
+    const char* argv[] = {
+        "/bin/sh",
+        NULL
+    };
+    BOOST_CHECK(lxc.start(argv));
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::RUNNING);
+    BOOST_CHECK(!lxc.shutdown(1));
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::RUNNING);
+    BOOST_CHECK(lxc.stop());
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED);
+
+    BOOST_CHECK(lxc.destroy());
+}
+
+BOOST_AUTO_TEST_CASE(StartHasStoppedTest)
+{
+    {
+        LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
+        BOOST_CHECK(lxc.create(TEMPLATE));
     }
     LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
-    BOOST_CHECK_EQUAL("STOPPED", lxc.getState());
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED);
+    const char* argv[] = {
+        "/bin/sh",
+        "-c",
+        "echo",
+        NULL
+    };
+    BOOST_CHECK(lxc.start(argv));
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::RUNNING);
+    waitForInit();
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED);
+
+    BOOST_CHECK(lxc.destroy());
+}
+
+BOOST_AUTO_TEST_CASE(FreezeUnfreezeTest)
+{
+    LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
+    BOOST_CHECK(lxc.create(TEMPLATE));
+    const char* argv[] = {
+        "/bin/sh",
+        "-c",
+        "trap exit SIGTERM; read",
+        NULL
+    };
+    BOOST_CHECK(lxc.start(argv));
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::RUNNING);
+    waitForInit();
+    BOOST_CHECK(lxc.freeze());
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::FROZEN);
+    BOOST_CHECK(lxc.unfreeze());
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::RUNNING);
+    BOOST_CHECK(lxc.shutdown(2));
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED);
+
+    BOOST_CHECK(lxc.destroy());
+}
+
+BOOST_AUTO_TEST_CASE(FreezeStopTest)
+{
+    LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
+    BOOST_CHECK(lxc.create(TEMPLATE));
+    const char* argv[] = {
+        "/bin/sh",
+        "-c",
+        "trap exit SIGTERM; read",
+        NULL
+    };
+    BOOST_CHECK(lxc.start(argv));
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::RUNNING);
+    waitForInit();
+    BOOST_CHECK(lxc.freeze());
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::FROZEN);
+    BOOST_CHECK(!lxc.shutdown(1));
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::FROZEN);
+    BOOST_CHECK(lxc.stop());
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED);
+
+    BOOST_CHECK(lxc.destroy());
+}
+
+BOOST_AUTO_TEST_CASE(RepeatTest)
+{
+    LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
+    BOOST_CHECK(lxc.create(TEMPLATE));
+    BOOST_CHECK(!lxc.create(TEMPLATE));// forbidden
     const char* argv[] = {
         "/bin/sh",
+        "-c",
+        "trap exit SIGTERM; read",
         NULL
     };
-    lxc.start(argv);
-    BOOST_CHECK_EQUAL("RUNNING", lxc.getState());
-    BOOST_CHECK_THROW(lxc.shutdown(1), LxcException);
-    BOOST_CHECK_EQUAL("RUNNING", lxc.getState());
-    lxc.stop();
-    BOOST_CHECK_EQUAL("STOPPED", lxc.getState());
-
-    lxc.destroy();
+    BOOST_CHECK(lxc.start(argv));
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::RUNNING);
+    waitForInit();
+    BOOST_CHECK(!lxc.start(argv)); // forbidden
+    BOOST_CHECK(lxc.freeze());
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::FROZEN);
+    BOOST_CHECK(lxc.freeze()); // repeat is nop
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::FROZEN);
+    BOOST_CHECK(lxc.unfreeze());
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::RUNNING);
+    BOOST_CHECK(lxc.unfreeze()); // repeat is nop
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::RUNNING);
+    BOOST_CHECK(lxc.stop());
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED);
+    BOOST_CHECK(lxc.stop()); // repeat is nop
+    BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED);
+
+    BOOST_CHECK(lxc.destroy());
+    BOOST_CHECK(!lxc.isDefined());
+    BOOST_CHECK(!lxc.destroy()); // forbidden (why?)
 }
 
 BOOST_AUTO_TEST_SUITE_END()
index 49cb292..b3f8a70 100644 (file)
@@ -43,14 +43,6 @@ CONFIGURE_FILE(ut-server/buggy-daemon.conf.in
               ${CMAKE_BINARY_DIR}/ut-server/buggy-daemon.conf @ONLY)
 FILE(GLOB server_manager_CONF_GEN ${CMAKE_BINARY_DIR}/ut-server/*.conf)
 
-CONFIGURE_FILE(ut-container-admin/containers/buggy.conf.in
-               ${CMAKE_BINARY_DIR}/ut-container-admin/containers/buggy.conf @ONLY)
-CONFIGURE_FILE(ut-container-admin/containers/test.conf.in
-               ${CMAKE_BINARY_DIR}/ut-container-admin/containers/test.conf @ONLY)
-CONFIGURE_FILE(ut-container-admin/containers/test-no-shutdown.conf.in
-               ${CMAKE_BINARY_DIR}/ut-container-admin/containers/test-no-shutdown.conf @ONLY)
-FILE(GLOB admin_container_CONF_GEN ${CMAKE_BINARY_DIR}/ut-container-admin/containers/*.conf)
-
 CONFIGURE_FILE(ut-network-admin/containers/test.conf.in
                ${CMAKE_BINARY_DIR}/ut-network-admin/containers/test.conf @ONLY)
 CONFIGURE_FILE(ut-network-admin/containers/buggy.conf.in
@@ -110,8 +102,6 @@ INSTALL(FILES        ${container_container_CONF_GEN}
 
 INSTALL(FILES        ${admin_container_CONF}
         DESTINATION  ${SC_TEST_CONFIG_INSTALL_DIR}/server/ut-container-admin/containers)
-INSTALL(FILES        ${admin_container_CONF_GEN}
-        DESTINATION  ${SC_TEST_CONFIG_INSTALL_DIR}/server/ut-container-admin/containers)
 
 INSTALL(FILES        ${network_container_CONF}
         DESTINATION  ${SC_TEST_CONFIG_INSTALL_DIR}/server/ut-network-admin/containers)
@@ -1,4 +1,7 @@
 {
+    "name" : "ut-container-admin-test",
+    "lxcTemplate" : "minimal.sh",
+    "initWithArgs" : ["/foo"],
     "privilege" : 10,
     "vt" : -1,
     "switchToDefaultAfterTimeout" : true,
index f4be18d..6ff36fe 100644 (file)
@@ -1,4 +1,7 @@
 {
+    "name" : "ut-container-admin-test",
+    "lxcTemplate" : "missing.sh",
+    "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; read"],
     "privilege" : 10,
     "vt" : -1,
     "switchToDefaultAfterTimeout" : true,
@@ -1,4 +1,7 @@
 {
+    "name" : "ut-container-admin-test",
+    "lxcTemplate" : "minimal.sh",
+    "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; read"],
     "privilege" : 10,
     "vt" : -1,
     "switchToDefaultAfterTimeout" : true,
index 2294b77..6d2ba6c 100644 (file)
  * @brief   Unit tests of the ContainerAdmin class
  */
 
-//#include "config.hpp"
-//#include "ut.hpp"
-//
-//#include "container-admin.hpp"
-//#include "exception.hpp"
-//
-//#include "utils/latch.hpp"
-//#include "utils/glib-loop.hpp"
-//#include "utils/exception.hpp"
-//#include "utils/callback-guard.hpp"
-//#include "libvirt/exception.hpp"
-//#include "config/manager.hpp"
-//
-//#include <memory>
-//#include <string>
-//#include <thread>
-//#include <chrono>
-//
-//
-//using namespace security_containers;
-//
-//namespace {
-//
-//const std::string TEST_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-container-admin/containers/test.conf";
-//const std::string TEST_NO_SHUTDOWN_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-container-admin/containers/test-no-shutdown.conf";
-//const std::string BUGGY_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-container-admin/containers/buggy.conf";
-//const std::string MISSING_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-container-admin/containers/missing.conf";
-//const unsigned int WAIT_TIMEOUT = 5 * 1000;
-//const unsigned int WAIT_STOP_TIMEOUT = 15 * 1000;
-//
-//void ensureStarted()
-//{
-//    std::this_thread::sleep_for(std::chrono::milliseconds(200));
-//}
-//
-//struct Fixture {
-//    utils::ScopedGlibLoop mLoop;
-//    utils::CallbackGuard mGuard;
-//};
-//
-//} // namespace
-//
-//
-//BOOST_FIXTURE_TEST_SUITE(ContainerAdminSuite, Fixture)
-//
-//BOOST_AUTO_TEST_CASE(ConstructorDestructorTest)
-//{
-//    ContainerConfig config;
-//    config::loadFromFile(TEST_CONFIG_PATH, config);
-//    std::unique_ptr<ContainerAdmin> admin;
-//    BOOST_REQUIRE_NO_THROW(admin.reset(new ContainerAdmin(config)));
-//    BOOST_REQUIRE_NO_THROW(admin.reset());
-//}
-//
-//BOOST_AUTO_TEST_CASE(BuggyConfigTest)
-//{
-//    ContainerConfig config;
-//    config::loadFromFile(BUGGY_CONFIG_PATH, config);
-//    BOOST_REQUIRE_THROW(ContainerAdmin ca(config), LibvirtOperationException);
-//}
-//
-//BOOST_AUTO_TEST_CASE(MissingConfigTest)
-//{
-//    ContainerConfig config;
-//    config::loadFromFile(MISSING_CONFIG_PATH, config);
-//    BOOST_REQUIRE_THROW(ContainerAdmin ca(config), UtilsException);
-//}
-//
-//BOOST_AUTO_TEST_CASE(StartTest)
-//{
-//    utils::Latch booted;
-//    ContainerAdmin::ListenerId id = ContainerAdmin::LISTENER_ID_INVALID;
-//    ContainerConfig config;
-//    config::loadFromFile(TEST_CONFIG_PATH, config);
-//    ContainerAdmin ca(config);
-//
-//    ContainerAdmin::LifecycleListener bootedListener = [&](const int event, const int detail) {
-//        if (event == VIR_DOMAIN_EVENT_STARTED && detail == VIR_DOMAIN_EVENT_STARTED_BOOTED) {
-//            booted.set();
-//        }
-//    };
-//    BOOST_REQUIRE_NO_THROW(id = ca.registerLifecycleListener(bootedListener, mGuard.spawn()));
-//
-//    BOOST_REQUIRE_NO_THROW(ca.start());
-//    ensureStarted();
-//
-//    BOOST_CHECK(booted.wait(WAIT_TIMEOUT));
-//    BOOST_CHECK(ca.isRunning());
-//
-//    BOOST_REQUIRE_NO_THROW(ca.removeListener(id));
-//}
-//
-//BOOST_AUTO_TEST_CASE(ShutdownTest)
-//{
-//    utils::Latch shutdown;
-//    ContainerAdmin::ListenerId id = ContainerAdmin::LISTENER_ID_INVALID;
-//    ContainerConfig config;
-//    config::loadFromFile(TEST_CONFIG_PATH, config);
-//    ContainerAdmin ca(config);
-//
-//    ContainerAdmin::LifecycleListener shutdownListener = [&](const int event, const int detail) {
-//        if (event == VIR_DOMAIN_EVENT_STOPPED && detail == VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN) {
-//            shutdown.set();
-//        }
-//    };
-//
-//    BOOST_REQUIRE_NO_THROW(ca.start());
-//    ensureStarted();
-//    BOOST_REQUIRE(ca.isRunning());
-//    BOOST_REQUIRE_NO_THROW(id = ca.registerLifecycleListener(shutdownListener, mGuard.spawn()));
-//
-//    BOOST_REQUIRE_NO_THROW(ca.shutdown());
-//    BOOST_CHECK(shutdown.wait(WAIT_TIMEOUT));
-//    BOOST_CHECK(!ca.isRunning());
-//    BOOST_CHECK(ca.isStopped());
-//
-//    BOOST_REQUIRE_NO_THROW(ca.removeListener(id));
-//}
-//
-//BOOST_AUTO_TEST_CASE(DestroyTest)
-//{
-//    utils::Latch destroyed;
-//    ContainerAdmin::ListenerId id = ContainerAdmin::LISTENER_ID_INVALID;
-//    ContainerConfig config;
-//    config::loadFromFile(TEST_CONFIG_PATH, config);
-//    ContainerAdmin ca(config);
-//
-//    ContainerAdmin::LifecycleListener destroyedListener = [&](const int event, const int detail) {
-//        if (event == VIR_DOMAIN_EVENT_STOPPED && detail == VIR_DOMAIN_EVENT_STOPPED_DESTROYED) {
-//            destroyed.set();
-//        }
-//    };
-//
-//    BOOST_REQUIRE_NO_THROW(ca.start());
-//    ensureStarted();
-//    BOOST_REQUIRE(ca.isRunning());
-//    BOOST_REQUIRE_NO_THROW(id = ca.registerLifecycleListener(destroyedListener, mGuard.spawn()));
-//
-//    BOOST_REQUIRE_NO_THROW(ca.destroy());
-//    BOOST_CHECK(destroyed.wait(WAIT_TIMEOUT));
-//    BOOST_CHECK(!ca.isRunning());
-//    BOOST_CHECK(ca.isStopped());
-//
-//    BOOST_REQUIRE_NO_THROW(ca.removeListener(id));
-//}
-//
-//BOOST_AUTO_TEST_CASE(StopShutdownTest)
-//{
-//    utils::Latch shutdown;
-//    ContainerAdmin::ListenerId id = ContainerAdmin::LISTENER_ID_INVALID;
-//    ContainerConfig config;
-//    config::loadFromFile(TEST_CONFIG_PATH, config);
-//    ContainerAdmin ca(config);
-//
-//    ContainerAdmin::LifecycleListener shutdownListener = [&](const int event, const int detail) {
-//        if (event == VIR_DOMAIN_EVENT_STOPPED && detail == VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN) {
-//            shutdown.set();
-//        }
-//    };
-//
-//    BOOST_REQUIRE_NO_THROW(ca.start());
-//    ensureStarted();
-//    BOOST_REQUIRE(ca.isRunning());
-//    BOOST_REQUIRE_NO_THROW(id = ca.registerLifecycleListener(shutdownListener, mGuard.spawn()));
-//
-//    BOOST_REQUIRE_NO_THROW(ca.stop());
-//    BOOST_CHECK(shutdown.wait(WAIT_TIMEOUT));
-//    BOOST_CHECK(!ca.isRunning());
-//    BOOST_CHECK(ca.isStopped());
-//
-//    BOOST_REQUIRE_NO_THROW(ca.removeListener(id));
-//}
-//
-//// This test needs to wait for a shutdown timer in stop() method. This takes 10s+.
-//BOOST_AUTO_TEST_CASE(StopDestroyTest)
-//{
-//    utils::Latch destroyed;
-//    ContainerAdmin::ListenerId id = ContainerAdmin::LISTENER_ID_INVALID;
-//    ContainerConfig config;
-//    config::loadFromFile(TEST_NO_SHUTDOWN_CONFIG_PATH, config);
-//    ContainerAdmin ca(config);
-//
-//    ContainerAdmin::LifecycleListener destroyedListener = [&](const int event, const int detail) {
-//        if (event == VIR_DOMAIN_EVENT_STOPPED && detail == VIR_DOMAIN_EVENT_STOPPED_DESTROYED) {
-//            destroyed.set();
-//        }
-//    };
-//
-//    BOOST_REQUIRE_NO_THROW(ca.start());
-//    ensureStarted();
-//    BOOST_REQUIRE(ca.isRunning());
-//    BOOST_REQUIRE_NO_THROW(id = ca.registerLifecycleListener(destroyedListener, mGuard.spawn()));
-//
-//    BOOST_REQUIRE_NO_THROW(ca.stop());
-//    BOOST_CHECK(destroyed.wait(WAIT_STOP_TIMEOUT));
-//    BOOST_CHECK(!ca.isRunning());
-//    BOOST_CHECK(ca.isStopped());
-//
-//    BOOST_REQUIRE_NO_THROW(ca.removeListener(id));
-//}
-//
-//BOOST_AUTO_TEST_CASE(SuspendTest)
-//{
-//    utils::Latch paused;
-//    ContainerAdmin::ListenerId id = ContainerAdmin::LISTENER_ID_INVALID;
-//    ContainerConfig config;
-//    config::loadFromFile(TEST_CONFIG_PATH, config);
-//    ContainerAdmin ca(config);
-//
-//    ContainerAdmin::LifecycleListener pausedListener = [&](const int event, const int detail) {
-//        if (event == VIR_DOMAIN_EVENT_SUSPENDED && detail == VIR_DOMAIN_EVENT_SUSPENDED_PAUSED) {
-//            paused.set();
-//        }
-//    };
-//
-//    BOOST_REQUIRE_NO_THROW(ca.start())
-//    ensureStarted();
-//    BOOST_REQUIRE(ca.isRunning());
-//    BOOST_REQUIRE_NO_THROW(id = ca.registerLifecycleListener(pausedListener, mGuard.spawn()));
-//
-//    BOOST_REQUIRE_NO_THROW(ca.suspend());
-//    BOOST_CHECK(paused.wait(WAIT_TIMEOUT));
-//    BOOST_CHECK(!ca.isRunning());
-//    BOOST_CHECK(ca.isPaused());
-//
-//    BOOST_REQUIRE_NO_THROW(ca.removeListener(id));
-//}
-//
-//BOOST_AUTO_TEST_CASE(ResumeTest)
-//{
-//    utils::Latch unpaused;
-//    ContainerAdmin::ListenerId id = ContainerAdmin::LISTENER_ID_INVALID;
-//    ContainerConfig config;
-//    config::loadFromFile(TEST_CONFIG_PATH, config);
-//    ContainerAdmin ca(config);
-//
-//    ContainerAdmin::LifecycleListener unpausedListener = [&](const int event, const int detail) {
-//        if (event == VIR_DOMAIN_EVENT_RESUMED && detail == VIR_DOMAIN_EVENT_RESUMED_UNPAUSED) {
-//            unpaused.set();
-//        }
-//    };
-//
-//    BOOST_REQUIRE_NO_THROW(ca.start());
-//    ensureStarted();
-//    BOOST_REQUIRE(ca.isRunning());
-//    BOOST_REQUIRE_NO_THROW(ca.suspend())
-//    BOOST_REQUIRE(ca.isPaused());
-//    BOOST_REQUIRE_NO_THROW(id = ca.registerLifecycleListener(unpausedListener, mGuard.spawn()));
-//
-//    BOOST_REQUIRE_NO_THROW(ca.resume());
-//    BOOST_CHECK(unpaused.wait(WAIT_TIMEOUT));
-//    BOOST_CHECK(!ca.isPaused());
-//    BOOST_CHECK(ca.isRunning());
-//
-//    BOOST_REQUIRE_NO_THROW(ca.removeListener(id));
-//}
-//
+#include "config.hpp"
+#include "ut.hpp"
+
+#include "container-admin.hpp"
+#include "exception.hpp"
+
+#include "utils/glib-loop.hpp"
+#include "utils/scoped-dir.hpp"
+#include "config/manager.hpp"
+
+using namespace security_containers;
+
+namespace {
+
+const std::string TEST_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-container-admin/containers/test.conf";
+const std::string TEST_NO_SHUTDOWN_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-container-admin/containers/test-no-shutdown.conf";
+const std::string BUGGY_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-container-admin/containers/buggy.conf";
+const std::string MISSING_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-container-admin/containers/missing.conf";
+const std::string CONTAINERS_PATH = "/tmp/ut-containers";
+const std::string LXC_TEMPLATES_PATH = SC_TEST_LXC_TEMPLATES_INSTALL_DIR;
+
+struct Fixture {
+    utils::ScopedGlibLoop mLoop;
+    utils::ScopedDir mContainersPathGuard = CONTAINERS_PATH;
+
+    ContainerConfig mConfig;
+
+    std::unique_ptr<ContainerAdmin> create(const std::string& configPath)
+    {
+        config::loadFromFile(configPath, mConfig);
+        return std::unique_ptr<ContainerAdmin>(new ContainerAdmin(CONTAINERS_PATH,
+                                                                  LXC_TEMPLATES_PATH,
+                                                                  mConfig));
+    }
+
+    void ensureStarted()
+    {
+        // wait for containers init to fully start
+        std::this_thread::sleep_for(std::chrono::milliseconds(200));
+    }
+};
+
+} // namespace
+
+
+BOOST_FIXTURE_TEST_SUITE(ContainerAdminSuite, Fixture)
+
+BOOST_AUTO_TEST_CASE(ConstructorDestructorTest)
+{
+    auto admin = create(TEST_CONFIG_PATH);
+    admin.reset();
+}
+
+BOOST_AUTO_TEST_CASE(MissingConfigTest)
+{
+    BOOST_REQUIRE_THROW(create(MISSING_CONFIG_PATH), ContainerOperationException);
+}
+
+BOOST_AUTO_TEST_CASE(StartTest)
+{
+    auto admin = create(TEST_CONFIG_PATH);
+
+    admin->start();
+    ensureStarted();
+
+    BOOST_CHECK(admin->isRunning());
+}
+
+BOOST_AUTO_TEST_CASE(StartBuggyTest)
+{
+    auto admin = create(BUGGY_CONFIG_PATH);
+    BOOST_REQUIRE_THROW(admin->start(), ContainerOperationException);
+}
+
+BOOST_AUTO_TEST_CASE(StopShutdownTest)
+{
+    auto admin = create(TEST_CONFIG_PATH);
+
+    admin->start();
+    ensureStarted();
+    BOOST_REQUIRE(admin->isRunning());
+
+    admin->stop();
+    BOOST_CHECK(!admin->isRunning());
+    BOOST_CHECK(admin->isStopped());
+}
+
+// This test needs to wait for a shutdown timer in stop() method. This takes 10s+.
+BOOST_AUTO_TEST_CASE(StopDestroyTest)
+{
+    auto admin = create(TEST_NO_SHUTDOWN_CONFIG_PATH);
+
+    admin->start();
+    ensureStarted();
+    BOOST_REQUIRE(admin->isRunning());
+
+    admin->stop();
+    BOOST_CHECK(!admin->isRunning());
+    BOOST_CHECK(admin->isStopped());
+}
+
+BOOST_AUTO_TEST_CASE(SuspendResumeTest)
+{
+    auto admin = create(TEST_NO_SHUTDOWN_CONFIG_PATH);
+
+    admin->start();
+    ensureStarted();
+    BOOST_REQUIRE(admin->isRunning());
+
+    admin->suspend();
+    BOOST_CHECK(!admin->isRunning());
+    BOOST_CHECK(!admin->isStopped());
+    BOOST_CHECK(admin->isPaused());
+
+    admin->resume();
+    BOOST_CHECK(!admin->isPaused());
+    BOOST_CHECK(!admin->isStopped());
+    BOOST_CHECK(admin->isRunning());
+}
+
 //BOOST_AUTO_TEST_CASE(SchedulerLevelTest)
 //{
-//    ContainerConfig config;
-//    config::loadFromFile(TEST_CONFIG_PATH, config);
-//    ContainerAdmin ca(config);
-//    BOOST_REQUIRE_NO_THROW(ca.start());
+//    auto admin = create(TEST_CONFIG_PATH);
+//
+//    admin->start();
 //    ensureStarted();
-//    BOOST_REQUIRE_NO_THROW(ca.setSchedulerLevel(SchedulerLevel::FOREGROUND));
-//    BOOST_REQUIRE(ca.getSchedulerQuota() == config.cpuQuotaForeground);
-//    BOOST_REQUIRE_NO_THROW(ca.setSchedulerLevel(SchedulerLevel::BACKGROUND));
-//    BOOST_REQUIRE(ca.getSchedulerQuota() == config.cpuQuotaBackground);
+//    BOOST_REQUIRE_NO_THROW(admin->setSchedulerLevel(SchedulerLevel::FOREGROUND));
+//    BOOST_REQUIRE(admin->getSchedulerQuota() == config.cpuQuotaForeground);
+//    BOOST_REQUIRE_NO_THROW(admin->setSchedulerLevel(SchedulerLevel::BACKGROUND));
+//    BOOST_REQUIRE(admin->getSchedulerQuota() == config.cpuQuotaBackground);
 //}
-//
-//BOOST_AUTO_TEST_SUITE_END()
+
+BOOST_AUTO_TEST_SUITE_END()
index dc2db18..fe5aa64 100644 (file)
@@ -52,11 +52,6 @@ const std::string MISSING_CONFIG_PATH = "/this/is/a/missing/file/path/config.con
 const std::string CONTAINERS_PATH = "/tmp/ut-containers";
 const std::string LXC_TEMPLATES_PATH = SC_TEST_LXC_TEMPLATES_INSTALL_DIR;
 
-void ensureStarted()
-{
-    std::this_thread::sleep_for(std::chrono::milliseconds(200));
-}
-
 struct Fixture {
     utils::ScopedGlibLoop mLoop;
     utils::ScopedDir mContainersPathGuard = CONTAINERS_PATH;
@@ -69,6 +64,12 @@ struct Fixture {
                                                         LXC_TEMPLATES_PATH,
                                                         ""));
     }
+
+    void ensureStarted()
+    {
+        // wait for containers init to fully start
+        std::this_thread::sleep_for(std::chrono::milliseconds(200));
+    }
 };
 
 } // namespace
@@ -84,7 +85,7 @@ BOOST_AUTO_TEST_CASE(ConstructorDestructorTest)
 
 BOOST_AUTO_TEST_CASE(BuggyConfigTest)
 {
-    BOOST_REQUIRE_THROW(create(BUGGY_CONFIG_PATH), std::exception);//TODO which one?
+    BOOST_REQUIRE_THROW(create(BUGGY_CONFIG_PATH), ContainerOperationException);
 }
 
 BOOST_AUTO_TEST_CASE(MissingConfigTest)