From 3cce24ba314a579c5fedd44e75c00b737cedd304 Mon Sep 17 00:00:00 2001 From: Lukasz Kostyra Date: Wed, 10 Jun 2015 13:25:25 +0200 Subject: [PATCH] Remove ZoneAdmin class [Feature] Removed ZoneAdmin class [Cause] Unneeded layer between Zone class and LXC Zone [Solution] Remove ZoneAdmin class, move its functionality to Zone class [Verification] Build, install, run tests Change-Id: Id539ee2596c948f4cac6a0b9dff198d3d2b42c75 --- server/zone-admin.cpp | 323 ------------------------------ server/zone-admin.hpp | 192 ------------------ server/zone.cpp | 224 ++++++++++++++++++--- server/zone.hpp | 42 +++- server/zones-manager.cpp | 1 - tests/unit_tests/server/ut-zone-admin.cpp | 174 ---------------- tests/unit_tests/server/ut-zone.cpp | 95 ++++++++- 7 files changed, 314 insertions(+), 737 deletions(-) delete mode 100644 server/zone-admin.cpp delete mode 100644 server/zone-admin.hpp delete mode 100644 tests/unit_tests/server/ut-zone-admin.cpp diff --git a/server/zone-admin.cpp b/server/zone-admin.cpp deleted file mode 100644 index 05d9c7d..0000000 --- a/server/zone-admin.cpp +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved - * - * Contact: Jan Olszak - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -/** - * @file - * @author Jan Olszak (j.olszak@samsung.com) - * @brief Implementation of class for administrating one zone - */ - -#include "config.hpp" - -#include "zone-admin.hpp" -#include "exception.hpp" -#include "netdev.hpp" - -#include "logger/logger.hpp" -#include "utils/paths.hpp" -#include "utils/c-array.hpp" -#include "lxc/cgroup.hpp" - -#include -#include -#include -#include - - -namespace vasum { - -const std::uint64_t DEFAULT_CPU_SHARES = 1024; -const std::uint64_t DEFAULT_VCPU_PERIOD_MS = 100000; - -ZoneAdmin::ZoneAdmin(const std::string& zoneId, - const std::string& zonesPath, - const std::string& zoneTemplateDir, - const ZoneConfig& config, - const ZoneDynamicConfig& dynamicConfig) - : mConfig(config), - mDynamicConfig(dynamicConfig), - mZone(zonesPath, zoneId), - mId(zoneId), - mDetachOnExit(false), - mDestroyOnExit(false) -{ - LOGD(mId << ": Instantiating ZoneAdmin object"); - - if (!mZone.isDefined()) { - - const std::string zoneTemplate = utils::getAbsolutePath(config.zoneTemplate, - zoneTemplateDir); - LOGI(mId << ": Creating zone from template: " << zoneTemplate); - utils::CStringArrayBuilder args; - if (!dynamicConfig.ipv4Gateway.empty()) { - args.add("--ipv4-gateway"); - args.add(dynamicConfig.ipv4Gateway.c_str()); - } - if (!dynamicConfig.ipv4.empty()) { - args.add("--ipv4"); - args.add(dynamicConfig.ipv4.c_str()); - } - const std::string vt = std::to_string(dynamicConfig.vt); - if (dynamicConfig.vt > 0) { - args.add("--vt"); - args.add(vt.c_str()); - } - if (!mZone.create(zoneTemplate, args.c_array())) { - throw ZoneOperationException("Could not create zone"); - } - } -} - - -ZoneAdmin::~ZoneAdmin() -{ - LOGD(mId << ": Destroying ZoneAdmin object..."); - - if (mDestroyOnExit) { - if (!mZone.stop()) { - LOGE(mId << ": Failed to stop the zone"); - } - if (!mZone.destroy()) { - LOGE(mId << ": Failed to destroy the zone"); - } - } - - if (!mDetachOnExit) { - // Try to forcefully stop - if (!mZone.stop()) { - LOGE(mId << ": Failed to stop the zone"); - } - } - - LOGD(mId << ": ZoneAdmin object destroyed"); -} - - -const std::string& ZoneAdmin::getId() const -{ - return mId; -} - - -void ZoneAdmin::start() -{ - LOGD(mId << ": Starting..."); - if (isRunning()) { - LOGD(mId << ": Already running - nothing to do..."); - return; - } - - utils::CStringArrayBuilder args; - for (const std::string& arg : mConfig.initWithArgs) { - args.add(arg.c_str()); - } - if (args.empty()) { - args.add("/sbin/init"); - } - - if (!mZone.start(args.c_array())) { - throw ZoneOperationException("Could not start zone"); - } - - // Wait until the full platform launch with graphical stack. - // VT should be activated by a graphical stack. - // If we do it with 'zoneToFocus.activateVT' before starting the graphical stack, - // graphical stack initialization failed and we finally switch to the black screen. - // Skip waiting when graphical stack is not running (unit tests). - if (mDynamicConfig.vt > 0) { - // TODO, timeout is a temporary solution - std::this_thread::sleep_for(std::chrono::milliseconds(4000)); - } - - LOGD(mId << ": Started"); -} - - -void ZoneAdmin::stop() -{ - LOGD(mId << ": Stopping procedure started..."); - if (isStopped()) { - LOGD(mId << ": Already crashed/down/off - nothing to do"); - return; - } - - if (!mZone.shutdown(mConfig.shutdownTimeout)) { - // force stop - if (!mZone.stop()) { - throw ZoneOperationException("Could not stop zone"); - } - } - - LOGD(mId << ": Stopping procedure ended"); -} - - -void ZoneAdmin::destroy() -{ - LOGD(mId << ": Destroying procedure started..."); - - if (!mZone.destroy()) { - throw ZoneOperationException("Could not destroy zone"); - } - - LOGD(mId << ": Destroying procedure ended"); -} - - -bool ZoneAdmin::isRunning() -{ - return mZone.getState() == lxc::LxcZone::State::RUNNING; -} - - -bool ZoneAdmin::isStopped() -{ - return mZone.getState() == lxc::LxcZone::State::STOPPED; -} - - -void ZoneAdmin::suspend() -{ - LOGD(mId << ": Pausing..."); - if (!mZone.freeze()) { - throw ZoneOperationException("Could not pause zone"); - } - LOGD(mId << ": Paused"); -} - - -void ZoneAdmin::resume() -{ - LOGD(mId << ": Resuming..."); - if (!mZone.unfreeze()) { - throw ZoneOperationException("Could not resume zone"); - } - LOGD(mId << ": Resumed"); -} - - -bool ZoneAdmin::isPaused() -{ - return mZone.getState() == lxc::LxcZone::State::FROZEN; -} - - -void ZoneAdmin::setSchedulerLevel(SchedulerLevel sched) -{ - assert(isRunning()); - - switch (sched) { - case SchedulerLevel::FOREGROUND: - LOGD(mId << ": Setting SchedulerLevel::FOREGROUND"); - setSchedulerParams(DEFAULT_CPU_SHARES, - DEFAULT_VCPU_PERIOD_MS, - mConfig.cpuQuotaForeground); - break; - case SchedulerLevel::BACKGROUND: - LOGD(mId << ": Setting SchedulerLevel::BACKGROUND"); - setSchedulerParams(DEFAULT_CPU_SHARES, - DEFAULT_VCPU_PERIOD_MS, - mConfig.cpuQuotaBackground); - break; - default: - assert(0 && "Unknown sched parameter value"); - } -} - - -void ZoneAdmin::setSchedulerParams(std::uint64_t cpuShares, - std::uint64_t vcpuPeriod, - std::int64_t vcpuQuota) -{ - assert(vcpuPeriod >= 1000 && vcpuPeriod <= 1000000); - assert(vcpuQuota == -1 || - (vcpuQuota >= 1000 && vcpuQuota <= static_cast(ULLONG_MAX / 1000))); - - if (!lxc::setCgroup(mId, "cpu", "cpu.shares", std::to_string(cpuShares)) || - !lxc::setCgroup(mId, "cpu", "cpu.cfs_period_us", std::to_string(vcpuPeriod)) || - !lxc::setCgroup(mId, "cpu", "cpu.cfs_quota_us", std::to_string(vcpuQuota))) { - - LOGE(mId << ": Error while setting the zone's scheduler params"); - throw ZoneOperationException("Could not set scheduler params"); - } -} - -void ZoneAdmin::setDetachOnExit() -{ - mDetachOnExit = true; -} - -void ZoneAdmin::setDestroyOnExit() -{ - mDestroyOnExit = true; -} - -std::int64_t ZoneAdmin::getSchedulerQuota() -{ - std::string ret; - if (!lxc::getCgroup(mId, "cpu", "cpu.cfs_quota_us", ret)) { - LOGE(mId << ": Error while getting the zone's scheduler quota param"); - throw ZoneOperationException("Could not get scheduler quota param"); - } - return std::stoll(ret); -} - -void ZoneAdmin::createNetdevVeth(const std::string& zoneDev, - const std::string& hostDev) -{ - netdev::createVeth(mZone.getInitPid(), zoneDev, hostDev); -} - -void ZoneAdmin::createNetdevMacvlan(const std::string& zoneDev, - const std::string& hostDev, - const uint32_t& mode) -{ - netdev::createMacvlan(mZone.getInitPid(), zoneDev, hostDev, static_cast(mode)); -} - -void ZoneAdmin::moveNetdev(const std::string& devId) -{ - netdev::movePhys(mZone.getInitPid(), devId); -} - -void ZoneAdmin::destroyNetdev(const std::string& devId) -{ - netdev::destroyNetdev(devId, mZone.getInitPid()); -} - -void ZoneAdmin::setNetdevAttrs(const std::string& netdev, const NetdevAttrs& attrs) -{ - netdev::setAttrs(mZone.getInitPid(), netdev, attrs); -} - -ZoneAdmin::NetdevAttrs ZoneAdmin::getNetdevAttrs(const std::string& netdev) -{ - return netdev::getAttrs(mZone.getInitPid(), netdev); -} - -std::vector ZoneAdmin::getNetdevList() -{ - return netdev::listNetdev(mZone.getInitPid()); -} - -void ZoneAdmin::deleteNetdevIpAddress(const std::string& netdev, const std::string& ip) -{ - netdev::deleteIpAddress(mZone.getInitPid(), netdev, ip); -} - -} // namespace vasum diff --git a/server/zone-admin.hpp b/server/zone-admin.hpp deleted file mode 100644 index ec4c7cd..0000000 --- a/server/zone-admin.hpp +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved - * - * Contact: Jan Olszak - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -/** - * @file - * @author Jan Olszak (j.olszak@samsung.com) - * @brief Declaration of the class for administrating one zone - */ - - -#ifndef SERVER_ZONE_ADMIN_HPP -#define SERVER_ZONE_ADMIN_HPP - -#include "zone-config.hpp" -#include "lxc/zone.hpp" -#include "netdev.hpp" - -namespace vasum { - - -enum class SchedulerLevel { - FOREGROUND, - BACKGROUND -}; - -class ZoneAdmin { - -public: - typedef netdev::Attrs NetdevAttrs; - - /** - * ZoneAdmin constructor - * @param zoneId zone id - * @param zonesPath directory where zones are defined (configs, rootfs etc) - * @param zoneTemplateDir directory where templates are stored - * @param config zones config - * @param dynamicConfig zones dynamic config - */ - ZoneAdmin(const std::string& zoneId, - const std::string& zonesPath, - const std::string& zoneTemplateDir, - const ZoneConfig& config, - const ZoneDynamicConfig& dynamicConfig); - virtual ~ZoneAdmin(); - - /** - * Get the zone id - */ - const std::string& getId() const; - - /** - * Boot the zone to the background. - */ - void start(); - - /** - * Try to shutdown the zone, if failed, kill it. - */ - void stop(); - - /** - * Destroy stopped zone. In particular it removes whole zones rootfs. - */ - void destroy(); - - /** - * @return Is the zone running? - */ - bool isRunning(); - - /** - * Check if the zone is stopped. It's NOT equivalent to !isRunning, - * because it checks different internal zone states. There are other states, - * (e.g. paused) when the zone isn't running nor stopped. - * - * @return Is the zone stopped? - */ - bool isStopped(); - - /** - * Suspends an active zone, the process is frozen - * without further access to CPU resources and I/O, - * but the memory used by the zone - * at the hypervisor level will stay allocated - */ - void suspend(); - - /** - * Resume the zone after suspension. - */ - void resume(); - - /** - * @return Is the zone in a paused state? - */ - bool isPaused(); - - /** - * Sets the zones scheduler CFS quota. - */ - void setSchedulerLevel(SchedulerLevel sched); - - /** - * Set whether zone should be detached on exit. - */ - void setDetachOnExit(); - - /** - * Set if zone should be destroyed on exit. - */ - void setDestroyOnExit(); - - /** - * @return Scheduler CFS quota, - * TODO: this function is only for UNIT TESTS - */ - std::int64_t getSchedulerQuota(); - - /** - * Create veth network device - */ - void createNetdevVeth(const std::string& zoneDev, - const std::string& hostDev); - - /** - * Create macvlan network device - */ - void createNetdevMacvlan(const std::string& zoneDev, - const std::string& hostDev, - const uint32_t& mode); - - /** - * Move network device to zone - */ - void moveNetdev(const std::string& devId); - - /** - * Destroy network device in zone - */ - void destroyNetdev(const std::string& devId); - - /** - * Set network device attributes - */ - void setNetdevAttrs(const std::string& netdev, const NetdevAttrs& attrs); - - /** - * Get network device attributes - */ - NetdevAttrs getNetdevAttrs(const std::string& netdev); - - /** - * Get network device list - */ - std::vector getNetdevList(); - - /** - * Remove ipv4/ipv6 address from network device - */ - void deleteNetdevIpAddress(const std::string& netdev, const std::string& ip); - -private: - const ZoneConfig& mConfig; - const ZoneDynamicConfig& mDynamicConfig; - lxc::LxcZone mZone; - const std::string mId; - bool mDetachOnExit; - bool mDestroyOnExit; - - void setSchedulerParams(std::uint64_t cpuShares, std::uint64_t vcpuPeriod, std::int64_t vcpuQuota); -}; - - -} // namespace vasum - - -#endif // SERVER_ZONE_ADMIN_HPP diff --git a/server/zone.cpp b/server/zone.cpp index 3fe0b12..94f6873 100644 --- a/server/zone.cpp +++ b/server/zone.cpp @@ -26,17 +26,22 @@ #include "zone.hpp" #include "dynamic-config-scheme.hpp" -#include "base-exception.hpp" +#include "exception.hpp" #include "logger/logger.hpp" #include "utils/paths.hpp" #include "utils/vt.hpp" +#include "utils/c-array.hpp" +#include "lxc/cgroup.hpp" #include "config/manager.hpp" #include +#include +#include #include #include +#include namespace vasum { @@ -50,6 +55,9 @@ const std::string STATE_STOPPED = "stopped"; const std::string STATE_RUNNING = "running"; const std::string STATE_PAUSED = "paused"; +const std::uint64_t DEFAULT_CPU_SHARES = 1024; +const std::uint64_t DEFAULT_VCPU_PERIOD_MS = 100000; + } // namespace Zone::Zone(const std::string& zoneId, @@ -59,7 +67,13 @@ Zone::Zone(const std::string& zoneId, const std::string& zoneTemplateDir, const std::string& baseRunMountPointPath) : mDbPath(dbPath) + , mZone(zonesPath, zoneId) + , mId(zoneId) + , mDetachOnExit(false) + , mDestroyOnExit(false) { + LOGD(mId << ": Instantiating Zone object"); + const std::string dbPrefix = getZoneDbPrefix(zoneId); config::loadFromKVStoreWithJsonFile(dbPath, zoneTemplatePath, mConfig, dbPrefix); config::loadFromKVStoreWithJsonFile(dbPath, zoneTemplatePath, mDynamicConfig, dbPrefix); @@ -75,7 +89,28 @@ Zone::Zone(const std::string& zoneId, mRunMountPoint = fs::absolute(mDynamicConfig.runMountPoint, baseRunMountPointPath).string(); } - mAdmin.reset(new ZoneAdmin(zoneId, zonesPath, zoneTemplateDir, mConfig, mDynamicConfig)); + if (!mZone.isDefined()) { + const std::string zoneTemplate = utils::getAbsolutePath(mConfig.zoneTemplate, + zoneTemplateDir); + LOGI(mId << ": Creating zone from template: " << zoneTemplate); + utils::CStringArrayBuilder args; + if (!mDynamicConfig.ipv4Gateway.empty()) { + args.add("--ipv4-gateway"); + args.add(mDynamicConfig.ipv4Gateway.c_str()); + } + if (!mDynamicConfig.ipv4.empty()) { + args.add("--ipv4"); + args.add(mDynamicConfig.ipv4.c_str()); + } + const std::string vt = std::to_string(mDynamicConfig.vt); + if (mDynamicConfig.vt > 0) { + args.add("--vt"); + args.add(vt.c_str()); + } + if (!mZone.create(zoneTemplate, args.c_array())) { + throw ZoneOperationException("Could not create zone"); + } + } const fs::path zonePath = fs::path(zonesPath) / zoneId; mRootPath = (zonePath / fs::path("rootfs")).string(); @@ -83,6 +118,29 @@ Zone::Zone(const std::string& zoneId, mProvision.reset(new ZoneProvision(mRootPath, zoneTemplatePath, dbPath, dbPrefix, mConfig.validLinkPrefixes)); } +Zone::~Zone() +{ + LOGD(mId << ": Destroying Zone object..."); + + if (mDestroyOnExit) { + if (!mZone.stop()) { + LOGE(mId << ": Failed to stop the zone"); + } + if (!mZone.destroy()) { + LOGE(mId << ": Failed to destroy the zone"); + } + } + + if (!mDetachOnExit && !mDestroyOnExit) { + // Try to forcefully stop + if (!mZone.stop()) { + LOGE(mId << ": Failed to stop the zone"); + } + } + + LOGD(mId << ": Zone object destroyed"); +} + const std::vector& Zone::getPermittedToSend() const { return mPermittedToSend; @@ -96,7 +154,7 @@ const std::vector& Zone::getPermittedToRecv() const const std::string& Zone::getId() const { Lock lock(mReconnectMutex); - return mAdmin->getId(); + return mId; } int Zone::getPrivilege() const @@ -106,14 +164,14 @@ int Zone::getPrivilege() const void Zone::saveDynamicConfig() { - config::saveToKVStore(mDbPath, mDynamicConfig, getZoneDbPrefix(getId())); + config::saveToKVStore(mDbPath, mDynamicConfig, getZoneDbPrefix(mId)); } void Zone::updateRequestedState(const std::string& state) { // assume mutex is locked if (state != mDynamicConfig.requestedState) { - LOGT("Set requested state of " << getId() << " to " << state); + LOGT("Set requested state of " << mId << " to " << state); mDynamicConfig.requestedState = state; saveDynamicConfig(); } @@ -125,7 +183,7 @@ void Zone::restore() { Lock lock(mReconnectMutex); requestedState = mDynamicConfig.requestedState; - LOGT("Requested state of " << getId() << ": " << requestedState); + LOGT("Requested state of " << mId << ": " << requestedState); } if (requestedState == STATE_RUNNING) { @@ -143,9 +201,41 @@ void Zone::restore() void Zone::start() { Lock lock(mReconnectMutex); + + LOGD(mId << ": Starting..."); + updateRequestedState(STATE_RUNNING); mProvision->start(); - mAdmin->start(); + + if (isRunning()) { + LOGD(mId << ": Already running - nothing to do..."); + return; + } + + utils::CStringArrayBuilder args; + for (const std::string& arg : mConfig.initWithArgs) { + args.add(arg.c_str()); + } + if (args.empty()) { + args.add("/sbin/init"); + } + + if (!mZone.start(args.c_array())) { + throw ZoneOperationException("Could not start zone"); + } + + // Wait until the full platform launch with graphical stack. + // VT should be activated by a graphical stack. + // If we do it with 'zoneToFocus.activateVT' before starting the graphical stack, + // graphical stack initialization failed and we finally switch to the black screen. + // Skip waiting when graphical stack is not running (unit tests). + if (mDynamicConfig.vt > 0) { + // TODO, timeout is a temporary solution + std::this_thread::sleep_for(std::chrono::milliseconds(4000)); + } + + LOGD(mId << ": Started"); + // Increase cpu quota before connect, otherwise it'd take ages. goForeground(); // refocus in ZonesManager will adjust cpu quota after all @@ -154,15 +244,32 @@ void Zone::start() void Zone::stop(bool saveState) { Lock lock(mReconnectMutex); + + LOGD(mId << ": Stopping procedure started..."); + if (saveState) { updateRequestedState(STATE_STOPPED); } - if (mAdmin->isRunning()) { + if (isRunning()) { // boost stopping goForeground(); } - mAdmin->stop(); + + if (isStopped()) { + LOGD(mId << ": Already crashed/down/off - nothing to do"); + return; + } + + if (!mZone.shutdown(mConfig.shutdownTimeout)) { + // force stop + if (!mZone.stop()) { + throw ZoneOperationException("Could not stop zone"); + } + } + mProvision->stop(); + + LOGD(mId << ": Stopping procedure ended"); } int Zone::getVT() const @@ -191,7 +298,7 @@ void Zone::createNetdevVeth(const std::string& zoneDev, const std::string& hostDev) { Lock lock(mReconnectMutex); - mAdmin->createNetdevVeth(zoneDev, hostDev); + netdev::createVeth(mZone.getInitPid(), zoneDev, hostDev); } void Zone::createNetdevMacvlan(const std::string& zoneDev, @@ -199,75 +306,87 @@ void Zone::createNetdevMacvlan(const std::string& zoneDev, const uint32_t& mode) { Lock lock(mReconnectMutex); - mAdmin->createNetdevMacvlan(zoneDev, hostDev, mode); + netdev::createMacvlan(mZone.getInitPid(), zoneDev, hostDev, static_cast(mode)); } void Zone::moveNetdev(const std::string& devId) { Lock lock(mReconnectMutex); - mAdmin->moveNetdev(devId); + netdev::movePhys(mZone.getInitPid(), devId); } void Zone::destroyNetdev(const std::string& devId) { Lock lock(mReconnectMutex); - mAdmin->destroyNetdev(devId); + netdev::destroyNetdev(devId, mZone.getInitPid()); } void Zone::goForeground() { Lock lock(mReconnectMutex); - mAdmin->setSchedulerLevel(SchedulerLevel::FOREGROUND); + setSchedulerLevel(SchedulerLevel::FOREGROUND); } void Zone::goBackground() { Lock lock(mReconnectMutex); - mAdmin->setSchedulerLevel(SchedulerLevel::BACKGROUND); + setSchedulerLevel(SchedulerLevel::BACKGROUND); } void Zone::setDetachOnExit() { Lock lock(mReconnectMutex); - mAdmin->setDetachOnExit(); + mDetachOnExit = true; } void Zone::setDestroyOnExit() { Lock lock(mReconnectMutex); - mAdmin->setDestroyOnExit(); + mDestroyOnExit = true; } bool Zone::isRunning() { Lock lock(mReconnectMutex); - return mAdmin->isRunning(); + return mZone.getState() == lxc::LxcZone::State::RUNNING; } bool Zone::isStopped() { Lock lock(mReconnectMutex); - return mAdmin->isStopped(); + return mZone.getState() == lxc::LxcZone::State::STOPPED; } void Zone::suspend() { Lock lock(mReconnectMutex); - mAdmin->suspend(); + + LOGD(mId << ": Pausing..."); + if (!mZone.freeze()) { + throw ZoneOperationException("Could not pause zone"); + } + LOGD(mId << ": Paused"); + updateRequestedState(STATE_PAUSED); } void Zone::resume() { Lock lock(mReconnectMutex); - mAdmin->resume(); + + LOGD(mId << ": Resuming..."); + if (!mZone.unfreeze()) { + throw ZoneOperationException("Could not resume zone"); + } + LOGD(mId << ": Resumed"); + updateRequestedState(STATE_RUNNING); } bool Zone::isPaused() { Lock lock(mReconnectMutex); - return mAdmin->isPaused(); + return mZone.getState() == lxc::LxcZone::State::FROZEN; } bool Zone::isSwitchToDefaultAfterTimeoutAllowed() const @@ -308,28 +427,77 @@ void Zone::removeDeclaration(const std::string& declarationId) mProvision->remove(declarationId); } -void Zone::setNetdevAttrs(const std::string& netdev, const ZoneAdmin::NetdevAttrs& attrs) +void Zone::setNetdevAttrs(const std::string& netdev, const NetdevAttrs& attrs) { Lock lock(mReconnectMutex); - mAdmin->setNetdevAttrs(netdev, attrs); + netdev::setAttrs(mZone.getInitPid(), netdev, attrs); } -ZoneAdmin::NetdevAttrs Zone::getNetdevAttrs(const std::string& netdev) +Zone::NetdevAttrs Zone::getNetdevAttrs(const std::string& netdev) { Lock lock(mReconnectMutex); - return mAdmin->getNetdevAttrs(netdev); + return netdev::getAttrs(mZone.getInitPid(), netdev); } std::vector Zone::getNetdevList() { Lock lock(mReconnectMutex); - return mAdmin->getNetdevList(); + return netdev::listNetdev(mZone.getInitPid()); } void Zone::deleteNetdevIpAddress(const std::string& netdev, const std::string& ip) { Lock lock(mReconnectMutex); - mAdmin->deleteNetdevIpAddress(netdev, ip); + netdev::deleteIpAddress(mZone.getInitPid(), netdev, ip); +} + +std::int64_t Zone::getSchedulerQuota() +{ + std::string ret; + if (!lxc::getCgroup(mId, "cpu", "cpu.cfs_quota_us", ret)) { + LOGE(mId << ": Error while getting the zone's scheduler quota param"); + throw ZoneOperationException("Could not get scheduler quota param"); + } + return std::stoll(ret); +} + +void Zone::setSchedulerLevel(SchedulerLevel sched) +{ + assert(isRunning()); + + switch (sched) { + case SchedulerLevel::FOREGROUND: + LOGD(mId << ": Setting SchedulerLevel::FOREGROUND"); + setSchedulerParams(DEFAULT_CPU_SHARES, + DEFAULT_VCPU_PERIOD_MS, + mConfig.cpuQuotaForeground); + break; + case SchedulerLevel::BACKGROUND: + LOGD(mId << ": Setting SchedulerLevel::BACKGROUND"); + setSchedulerParams(DEFAULT_CPU_SHARES, + DEFAULT_VCPU_PERIOD_MS, + mConfig.cpuQuotaBackground); + break; + default: + assert(0 && "Unknown sched parameter value"); + } +} + +void Zone::setSchedulerParams(std::uint64_t cpuShares, + std::uint64_t vcpuPeriod, + std::int64_t vcpuQuota) +{ + assert(vcpuPeriod >= 1000 && vcpuPeriod <= 1000000); + assert(vcpuQuota == -1 || + (vcpuQuota >= 1000 && vcpuQuota <= static_cast(ULLONG_MAX / 1000))); + + if (!lxc::setCgroup(mId, "cpu", "cpu.shares", std::to_string(cpuShares)) || + !lxc::setCgroup(mId, "cpu", "cpu.cfs_period_us", std::to_string(vcpuPeriod)) || + !lxc::setCgroup(mId, "cpu", "cpu.cfs_quota_us", std::to_string(vcpuQuota))) { + + LOGE(mId << ": Error while setting the zone's scheduler params"); + throw ZoneOperationException("Could not set scheduler params"); + } } } // namespace vasum diff --git a/server/zone.hpp b/server/zone.hpp index fb9f0c7..0829c35 100644 --- a/server/zone.hpp +++ b/server/zone.hpp @@ -27,9 +27,11 @@ #define SERVER_ZONE_HPP #include "zone-config.hpp" -#include "zone-admin.hpp" #include "zone-provision.hpp" +#include "lxc/zone.hpp" +#include "netdev.hpp" + #include #include #include @@ -40,9 +42,16 @@ namespace vasum { +enum class SchedulerLevel { + FOREGROUND, + BACKGROUND +}; + class Zone { public: + typedef netdev::Attrs NetdevAttrs; + /** * Zone constructor * @param zoneId zone id @@ -60,6 +69,7 @@ public: const std::string& baseRunMountPointPath); Zone(const Zone&) = delete; Zone& operator=(const Zone&) = delete; + ~Zone(); typedef std::function StartAsyncResultCallback; @@ -75,8 +85,6 @@ public: */ const std::vector& getPermittedToRecv() const; - // ZoneAdmin API - /** * Get the zone id */ @@ -124,8 +132,6 @@ public: /** * Set if zone should be detached on exit. - * - * This sends detach flag to ZoneAdmin object. */ void setDetachOnExit(); @@ -149,7 +155,10 @@ public: bool isStopped(); /** - * Suspend zone. + * Suspends an active zone, the process is frozen + * without further access to CPU resources and I/O, + * but the memory used by the zone + * at the hypervisor level will stay allocated */ void suspend(); @@ -235,12 +244,12 @@ public: /** * Set network device attributes */ - void setNetdevAttrs(const std::string& netdev, const ZoneAdmin::NetdevAttrs& attrs); + void setNetdevAttrs(const std::string& netdev, const NetdevAttrs& attrs); /** * Get network device attributes */ - ZoneAdmin::NetdevAttrs getNetdevAttrs(const std::string& netdev); + NetdevAttrs getNetdevAttrs(const std::string& netdev); /** * Get network device list @@ -252,21 +261,36 @@ public: */ void deleteNetdevIpAddress(const std::string& netdev, const std::string& ip); + /** + * Sets the zones scheduler CFS quota. + */ + void setSchedulerLevel(SchedulerLevel sched); + + /** + * @return Scheduler CFS quota, + * TODO: this function is only for UNIT TESTS + */ + std::int64_t getSchedulerQuota(); + private: ZoneConfig mConfig; ZoneDynamicConfig mDynamicConfig; std::vector mPermittedToSend; std::vector mPermittedToRecv; - std::unique_ptr mAdmin; std::unique_ptr mProvision; mutable std::recursive_mutex mReconnectMutex; std::string mRunMountPoint; std::string mRootPath; std::string mDbPath; + lxc::LxcZone mZone; + const std::string mId; + bool mDetachOnExit; + bool mDestroyOnExit; void onNameLostCallback(); void saveDynamicConfig(); void updateRequestedState(const std::string& state); + void setSchedulerParams(std::uint64_t cpuShares, std::uint64_t vcpuPeriod, std::int64_t vcpuQuota); }; diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index 7e0f641..a34dbe9 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -27,7 +27,6 @@ #include "common-definitions.hpp" #include "dynamic-config-scheme.hpp" #include "zones-manager.hpp" -#include "zone-admin.hpp" #include "lxc/cgroup.hpp" #include "exception.hpp" diff --git a/tests/unit_tests/server/ut-zone-admin.cpp b/tests/unit_tests/server/ut-zone-admin.cpp deleted file mode 100644 index faee59a..0000000 --- a/tests/unit_tests/server/ut-zone-admin.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved - * - * Contact: Jan Olszak - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - - -/** - * @file - * @author Jan Olszak (j.olszak@samsung.com) - * @brief Unit tests of the ZoneAdmin class - */ - -#include "config.hpp" -#include "ut.hpp" - -#include "zone-admin.hpp" -#include "exception.hpp" - -#include "utils/glib-loop.hpp" -#include "utils/scoped-dir.hpp" -#include "config/manager.hpp" - -using namespace vasum; - -namespace { - -const std::string TEMPLATES_DIR = VSM_TEST_TEMPLATES_INSTALL_DIR; -const std::string TEST_CONFIG_PATH = TEMPLATES_DIR + "/default.conf"; -const std::string TEST_NO_SHUTDOWN_CONFIG_PATH = TEMPLATES_DIR + "/test-no-shutdown.conf"; -const std::string BUGGY_CONFIG_PATH = TEMPLATES_DIR + "/buggy-init.conf"; -const std::string MISSING_CONFIG_PATH = TEMPLATES_DIR + "/missing.conf"; -const std::string ZONES_PATH = "/tmp/ut-zones"; - -struct Fixture { - utils::ScopedGlibLoop mLoop; - utils::ScopedDir mZonesPathGuard; - - ZoneConfig mConfig; - ZoneDynamicConfig mDynamicConfig; - - Fixture() - : mZonesPathGuard(ZONES_PATH) - {} - - std::unique_ptr create(const std::string& configPath) - { - config::loadFromJsonFile(configPath, mConfig); - config::loadFromJsonFile(configPath, mDynamicConfig); - return std::unique_ptr(new ZoneAdmin("zoneId", - ZONES_PATH, - TEMPLATES_DIR, - mConfig, - mDynamicConfig)); - } - - void ensureStarted() - { - // wait for zones init to fully start - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - } -}; - -} // namespace - - -BOOST_FIXTURE_TEST_SUITE(ZoneAdminSuite, Fixture) - -BOOST_AUTO_TEST_CASE(ConstructorDestructor) -{ - auto admin = create(TEST_CONFIG_PATH); - admin.reset(); -} - -BOOST_AUTO_TEST_CASE(MissingConfig) -{ - BOOST_REQUIRE_EXCEPTION(create(MISSING_CONFIG_PATH), - ZoneOperationException, - WhatEquals("Could not create zone")); -} - -BOOST_AUTO_TEST_CASE(Start) -{ - auto admin = create(TEST_CONFIG_PATH); - - admin->start(); - ensureStarted(); - - BOOST_CHECK(admin->isRunning()); -} - -BOOST_AUTO_TEST_CASE(StartBuggy) -{ - auto admin = create(BUGGY_CONFIG_PATH); - BOOST_REQUIRE_EXCEPTION(admin->start(), - ZoneOperationException, - WhatEquals("Could not start zone")); -} - -BOOST_AUTO_TEST_CASE(StopShutdown) -{ - 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(StopDestroy) -{ - 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(SuspendResume) -{ - 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(ForegroundBackgroundSchedulerLevel) -{ - auto admin = create(TEST_CONFIG_PATH); - - BOOST_REQUIRE(mConfig.cpuQuotaForeground != mConfig.cpuQuotaBackground); - - admin->start(); - ensureStarted(); - - admin->setSchedulerLevel(SchedulerLevel::FOREGROUND); - BOOST_CHECK_EQUAL(admin->getSchedulerQuota(), mConfig.cpuQuotaForeground); - - admin->setSchedulerLevel(SchedulerLevel::BACKGROUND); - BOOST_CHECK_EQUAL(admin->getSchedulerQuota(), mConfig.cpuQuotaBackground); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/unit_tests/server/ut-zone.cpp b/tests/unit_tests/server/ut-zone.cpp index abc10dc..1da6190 100644 --- a/tests/unit_tests/server/ut-zone.cpp +++ b/tests/unit_tests/server/ut-zone.cpp @@ -33,6 +33,7 @@ #include "utils/exception.hpp" #include "utils/glib-loop.hpp" #include "utils/scoped-dir.hpp" +#include "config/manager.hpp" #include "config/exception.hpp" #include "netdev.hpp" @@ -54,8 +55,10 @@ namespace { const std::string TEMPLATES_DIR = VSM_TEST_TEMPLATES_INSTALL_DIR; const std::string TEST_CONFIG_PATH = TEMPLATES_DIR + "/default.conf"; +const std::string TEST_NO_SHUTDOWN_CONFIG_PATH = TEMPLATES_DIR + "/test-no-shutdown.conf"; const std::string TEST_DBUS_CONFIG_PATH = TEMPLATES_DIR + "/console-dbus.conf"; const std::string BUGGY_CONFIG_PATH = TEMPLATES_DIR + "/buggy-template.conf"; +const std::string BUGGY_INIT_CONFIG_PATH = TEMPLATES_DIR + "/buggy-init.conf"; const std::string MISSING_CONFIG_PATH = TEMPLATES_DIR + "/missing-config.conf"; const std::string ZONES_PATH = "/tmp/ut-zones"; const std::string DB_PATH = ZONES_PATH + "/vasum.db"; @@ -144,6 +147,78 @@ BOOST_AUTO_TEST_CASE(StartStop) c->stop(true); } +BOOST_AUTO_TEST_CASE(StartBuggyInit) +{ + auto c = create(BUGGY_INIT_CONFIG_PATH); + BOOST_REQUIRE_EXCEPTION(c->start(), + ZoneOperationException, + WhatEquals("Could not start zone")); +} + +BOOST_AUTO_TEST_CASE(StopShutdown) +{ + auto c = create(TEST_CONFIG_PATH); + + c->start(); + ensureStarted(); + BOOST_REQUIRE(c->isRunning()); + + c->stop(true); + BOOST_CHECK(!c->isRunning()); + BOOST_CHECK(c->isStopped()); +} + +// This test needs to wait for a shutdown timer in stop() method. This takes 10s+. +BOOST_AUTO_TEST_CASE(StopDestroy) +{ + auto c = create(TEST_NO_SHUTDOWN_CONFIG_PATH); + + c->start(); + ensureStarted(); + BOOST_REQUIRE(c->isRunning()); + + c->stop(true); + BOOST_CHECK(!c->isRunning()); + BOOST_CHECK(c->isStopped()); +} + +BOOST_AUTO_TEST_CASE(SuspendResume) +{ + auto c = create(TEST_NO_SHUTDOWN_CONFIG_PATH); + + c->start(); + ensureStarted(); + BOOST_REQUIRE(c->isRunning()); + + c->suspend(); + BOOST_CHECK(!c->isRunning()); + BOOST_CHECK(!c->isStopped()); + BOOST_CHECK(c->isPaused()); + + c->resume(); + BOOST_CHECK(!c->isPaused()); + BOOST_CHECK(!c->isStopped()); + BOOST_CHECK(c->isRunning()); +} + +BOOST_AUTO_TEST_CASE(ForegroundBackgroundSchedulerLevel) +{ + auto c = create(TEST_CONFIG_PATH); + ZoneConfig refConfig; + config::loadFromJsonFile(TEST_CONFIG_PATH, refConfig); + + BOOST_REQUIRE(refConfig.cpuQuotaForeground != refConfig.cpuQuotaBackground); + + c->start(); + ensureStarted(); + + c->setSchedulerLevel(SchedulerLevel::FOREGROUND); + BOOST_CHECK_EQUAL(c->getSchedulerQuota(), refConfig.cpuQuotaForeground); + + c->setSchedulerLevel(SchedulerLevel::BACKGROUND); + BOOST_CHECK_EQUAL(c->getSchedulerQuota(), refConfig.cpuQuotaBackground); +} + BOOST_AUTO_TEST_CASE(DbusConnection) { mRunGuard.create("/tmp/ut-run"); // the same path as in zone template @@ -214,7 +289,7 @@ BOOST_AUTO_TEST_CASE(GetNetdevAttrs) c->start(); ensureStarted(); c->createNetdevVeth(ZONE_NETDEV, BRIDGE_NAME); - ZoneAdmin::NetdevAttrs attrs = c->getNetdevAttrs(ZONE_NETDEV); + Zone::NetdevAttrs attrs = c->getNetdevAttrs(ZONE_NETDEV); bool gotMtu = false; bool gotFlags = false; bool gotType = false; @@ -246,12 +321,12 @@ BOOST_AUTO_TEST_CASE(SetNetdevAttrs) c->start(); ensureStarted(); c->createNetdevVeth(ZONE_NETDEV, BRIDGE_NAME); - ZoneAdmin::NetdevAttrs attrsIn; + Zone::NetdevAttrs attrsIn; attrsIn.push_back(std::make_tuple("mtu", "500")); c->setNetdevAttrs(ZONE_NETDEV, attrsIn); bool gotMtu = false; - ZoneAdmin::NetdevAttrs attrsOut = c->getNetdevAttrs(ZONE_NETDEV); + Zone::NetdevAttrs attrsOut = c->getNetdevAttrs(ZONE_NETDEV); for (auto& attr : attrsOut) { if (std::get<0>(attr) == "mtu") { BOOST_CHECK(!gotMtu); @@ -275,11 +350,11 @@ BOOST_AUTO_TEST_CASE(SetNetdevIpv4) c->start(); ensureStarted(); c->createNetdevVeth(ZONE_NETDEV, BRIDGE_NAME); - ZoneAdmin::NetdevAttrs attrsIn; + Zone::NetdevAttrs attrsIn; attrsIn.push_back(std::make_tuple("ipv4", "ip:192.168.4.1,prefixlen:24")); c->setNetdevAttrs(ZONE_NETDEV, attrsIn); - ZoneAdmin::NetdevAttrs attrsOut = c->getNetdevAttrs(ZONE_NETDEV); + Zone::NetdevAttrs attrsOut = c->getNetdevAttrs(ZONE_NETDEV); int gotIp = 0; for (auto& attr : attrsOut) { if (std::get<0>(attr) == "ipv4") { @@ -315,11 +390,11 @@ BOOST_AUTO_TEST_CASE(SetNetdevIpv6) c->start(); ensureStarted(); c->createNetdevVeth(ZONE_NETDEV, BRIDGE_NAME); - ZoneAdmin::NetdevAttrs attrsIn; + Zone::NetdevAttrs attrsIn; attrsIn.push_back(std::make_tuple("ipv6", "ip:2001:db8::1,prefixlen:64")); c->setNetdevAttrs(ZONE_NETDEV, attrsIn); - ZoneAdmin::NetdevAttrs attrsOut = c->getNetdevAttrs(ZONE_NETDEV); + Zone::NetdevAttrs attrsOut = c->getNetdevAttrs(ZONE_NETDEV); int gotIp = 0; for (auto& attr : attrsOut) { if (std::get<0>(attr) == "ipv6") { @@ -350,10 +425,10 @@ BOOST_AUTO_TEST_CASE(SetNetdevIpv6) BOOST_AUTO_TEST_CASE(DelNetdevIpAddress) { - auto contain = [](const ZoneAdmin::NetdevAttrs& container, const std::string& key) { + auto contain = [](const Zone::NetdevAttrs& container, const std::string& key) { return container.end() != find_if(container.begin(), container.end(), - [&](const ZoneAdmin::NetdevAttrs::value_type& value) { + [&](const Zone::NetdevAttrs::value_type& value) { return std::get<0>(value) == key; }); }; @@ -363,7 +438,7 @@ BOOST_AUTO_TEST_CASE(DelNetdevIpAddress) c->start(); ensureStarted(); c->createNetdevVeth(ZONE_NETDEV, BRIDGE_NAME); - ZoneAdmin::NetdevAttrs attrs; + Zone::NetdevAttrs attrs; attrs.push_back(std::make_tuple("ipv6", "ip:2001:db8::1,prefixlen:64")); attrs.push_back(std::make_tuple("ipv4", "ip:192.168.4.1,prefixlen:24")); c->setNetdevAttrs(ZONE_NETDEV, attrs); -- 2.7.4