2 * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
4 * Contact: Lukasz Pawelczyk <l.pawelczyk@partner.samsung.com>
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License
21 * @author Lukasz Pawelczyk (l.pawelczyk@partner.samsung.com)
22 * @brief Implementation of class for managing one zone
28 #include "dynamic-config-scheme.hpp"
29 #include "exception.hpp"
31 #include "logger/logger.hpp"
32 #include "utils/paths.hpp"
33 #include "utils/vt.hpp"
34 #include "utils/c-array.hpp"
35 #include "lxc/cgroup.hpp"
36 #include "config/manager.hpp"
38 #include <boost/filesystem.hpp>
48 namespace fs = boost::filesystem;
52 typedef std::lock_guard<std::recursive_mutex> Lock;
54 const std::string STATE_STOPPED = "stopped";
55 const std::string STATE_RUNNING = "running";
56 const std::string STATE_PAUSED = "paused";
58 const std::uint64_t DEFAULT_CPU_SHARES = 1024;
59 const std::uint64_t DEFAULT_VCPU_PERIOD_MS = 100000;
63 Zone::Zone(const std::string& zoneId,
64 const std::string& zonesPath,
65 const std::string& zoneTemplatePath,
66 const std::string& dbPath,
67 const std::string& zoneTemplateDir,
68 const std::string& baseRunMountPointPath)
70 , mZone(zonesPath, zoneId)
72 , mDetachOnExit(false)
73 , mDestroyOnExit(false)
75 LOGD(mId << ": Instantiating Zone object");
77 const std::string dbPrefix = getZoneDbPrefix(zoneId);
78 config::loadFromKVStoreWithJsonFile(dbPath, zoneTemplatePath, mConfig, dbPrefix);
79 config::loadFromKVStoreWithJsonFile(dbPath, zoneTemplatePath, mDynamicConfig, dbPrefix);
81 if (!mDynamicConfig.runMountPoint.empty()) {
82 mRunMountPoint = fs::absolute(mDynamicConfig.runMountPoint, baseRunMountPointPath).string();
85 if (!mZone.isDefined()) {
86 const std::string zoneTemplate = utils::getAbsolutePath(mConfig.zoneTemplate,
88 LOGI(mId << ": Creating zone from template: " << zoneTemplate);
89 utils::CStringArrayBuilder args;
90 if (!mDynamicConfig.ipv4Gateway.empty()) {
91 args.add("--ipv4-gateway");
92 args.add(mDynamicConfig.ipv4Gateway.c_str());
94 if (!mDynamicConfig.ipv4.empty()) {
96 args.add(mDynamicConfig.ipv4.c_str());
98 const std::string vt = std::to_string(mDynamicConfig.vt);
99 if (mDynamicConfig.vt > 0) {
101 args.add(vt.c_str());
103 if (!mZone.create(zoneTemplate, args.c_array())) {
104 throw ZoneOperationException("Could not create zone");
108 const fs::path zonePath = fs::path(zonesPath) / zoneId;
109 mRootPath = (zonePath / fs::path("rootfs")).string();
111 mProvision.reset(new ZoneProvision(mRootPath, zoneTemplatePath, dbPath, dbPrefix, mConfig.validLinkPrefixes));
116 LOGD(mId << ": Destroying Zone object...");
118 if (mDestroyOnExit) {
120 LOGE(mId << ": Failed to stop the zone");
122 if (!mZone.destroy()) {
123 LOGE(mId << ": Failed to destroy the zone");
127 if (!mDetachOnExit && !mDestroyOnExit) {
128 // Try to forcefully stop
130 LOGE(mId << ": Failed to stop the zone");
134 LOGD(mId << ": Zone object destroyed");
137 const std::string& Zone::getId() const
139 Lock lock(mReconnectMutex);
143 int Zone::getPrivilege() const
145 return mConfig.privilege;
148 void Zone::saveDynamicConfig()
150 config::saveToKVStore(mDbPath, mDynamicConfig, getZoneDbPrefix(mId));
153 void Zone::updateRequestedState(const std::string& state)
155 // assume mutex is locked
156 if (state != mDynamicConfig.requestedState) {
157 LOGT("Set requested state of " << mId << " to " << state);
158 mDynamicConfig.requestedState = state;
165 std::string requestedState;
167 Lock lock(mReconnectMutex);
168 requestedState = mDynamicConfig.requestedState;
169 LOGT("Requested state of " << mId << ": " << requestedState);
172 if (requestedState == STATE_RUNNING) {
174 } else if (requestedState == STATE_STOPPED) {
175 } else if (requestedState == STATE_PAUSED) {
179 LOGE("Invalid requested state: " << requestedState);
180 assert(0 && "invalid requested state");
186 Lock lock(mReconnectMutex);
188 LOGD(mId << ": Starting...");
190 updateRequestedState(STATE_RUNNING);
194 LOGD(mId << ": Already running - nothing to do...");
198 utils::CStringArrayBuilder args;
199 for (const std::string& arg : mConfig.initWithArgs) {
200 args.add(arg.c_str());
203 args.add("/sbin/init");
206 if (!mZone.start(args.c_array())) {
207 std::string msg = "Could not start zone " + mZone.getName();
209 throw ZoneOperationException(msg);
212 // Wait until the full platform launch with graphical stack.
213 // VT should be activated by a graphical stack.
214 // If we do it with 'zoneToFocus.activateVT' before starting the graphical stack,
215 // graphical stack initialization failed and we finally switch to the black screen.
216 // Skip waiting when graphical stack is not running (unit tests).
217 if (mDynamicConfig.vt > 0) {
218 // TODO, timeout is a temporary solution
219 std::this_thread::sleep_for(std::chrono::milliseconds(4000));
222 LOGD(mId << ": Started");
224 // Increase cpu quota before connect, otherwise it'd take ages.
226 // refocus in ZonesManager will adjust cpu quota after all
229 void Zone::stop(bool saveState)
231 Lock lock(mReconnectMutex);
233 LOGD(mId << ": Stopping procedure started...");
236 updateRequestedState(STATE_STOPPED);
244 LOGD(mId << ": Already crashed/down/off - nothing to do");
248 if (!mZone.shutdown(mConfig.shutdownTimeout)) {
251 throw ZoneOperationException("Could not stop zone");
257 LOGD(mId << ": Stopping procedure ended");
260 int Zone::getVT() const
262 Lock lock(mReconnectMutex);
263 return mDynamicConfig.vt;
266 std::string Zone::getRootPath() const
271 bool Zone::activateVT()
273 Lock lock(mReconnectMutex);
275 if (mDynamicConfig.vt >= 0) {
276 return utils::activateVT(mDynamicConfig.vt);
282 void Zone::createNetdevVeth(const std::string& zoneDev,
283 const std::string& hostDev)
285 Lock lock(mReconnectMutex);
286 netdev::createVeth(mZone.getInitPid(), zoneDev, hostDev);
289 void Zone::createNetdevMacvlan(const std::string& zoneDev,
290 const std::string& hostDev,
291 const uint32_t& mode)
293 Lock lock(mReconnectMutex);
294 netdev::createMacvlan(mZone.getInitPid(), zoneDev, hostDev, static_cast<macvlan_mode>(mode));
297 void Zone::moveNetdev(const std::string& devId)
299 Lock lock(mReconnectMutex);
300 netdev::movePhys(mZone.getInitPid(), devId);
303 void Zone::destroyNetdev(const std::string& devId)
305 Lock lock(mReconnectMutex);
306 netdev::destroyNetdev(devId, mZone.getInitPid());
309 void Zone::goForeground()
311 Lock lock(mReconnectMutex);
312 setSchedulerLevel(SchedulerLevel::FOREGROUND);
315 void Zone::goBackground()
317 Lock lock(mReconnectMutex);
318 setSchedulerLevel(SchedulerLevel::BACKGROUND);
321 void Zone::setDetachOnExit()
323 Lock lock(mReconnectMutex);
324 mDetachOnExit = true;
327 void Zone::setDestroyOnExit()
329 Lock lock(mReconnectMutex);
330 mDestroyOnExit = true;
333 bool Zone::isRunning()
335 Lock lock(mReconnectMutex);
336 return mZone.getState() == lxc::LxcZone::State::RUNNING;
339 bool Zone::isStopped()
341 Lock lock(mReconnectMutex);
342 return mZone.getState() == lxc::LxcZone::State::STOPPED;
347 Lock lock(mReconnectMutex);
349 LOGD(mId << ": Pausing...");
350 if (!mZone.freeze()) {
351 throw ZoneOperationException("Could not pause zone");
353 LOGD(mId << ": Paused");
355 updateRequestedState(STATE_PAUSED);
360 Lock lock(mReconnectMutex);
362 LOGD(mId << ": Resuming...");
363 if (!mZone.unfreeze()) {
364 throw ZoneOperationException("Could not resume zone");
366 LOGD(mId << ": Resumed");
368 updateRequestedState(STATE_RUNNING);
371 bool Zone::isPaused()
373 Lock lock(mReconnectMutex);
374 return mZone.getState() == lxc::LxcZone::State::FROZEN;
377 bool Zone::isSwitchToDefaultAfterTimeoutAllowed() const
379 return mConfig.switchToDefaultAfterTimeout;
382 int Zone::createFile(const std::string& path, const std::int32_t flags, const std::int32_t mode)
385 if (!mZone.createFile(path, flags, mode, &fd)) {
386 throw ZoneOperationException("Failed to create file.");
391 std::string Zone::declareFile(const int32_t& type,
392 const std::string& path,
393 const int32_t& flags,
396 return mProvision->declareFile(type, path, flags, mode);
399 std::string Zone::declareMount(const std::string& source,
400 const std::string& target,
401 const std::string& type,
402 const int64_t& flags,
403 const std::string& data)
405 return mProvision->declareMount(source, target, type, flags, data);
408 std::string Zone::declareLink(const std::string& source,
409 const std::string& target)
411 return mProvision->declareLink(source, target);
414 std::vector<std::string> Zone::getDeclarations() const
416 return mProvision->list();
419 void Zone::removeDeclaration(const std::string& declarationId)
421 mProvision->remove(declarationId);
424 void Zone::setNetdevAttrs(const std::string& netdev, const NetdevAttrs& attrs)
426 Lock lock(mReconnectMutex);
427 netdev::setAttrs(mZone.getInitPid(), netdev, attrs);
430 Zone::NetdevAttrs Zone::getNetdevAttrs(const std::string& netdev)
432 Lock lock(mReconnectMutex);
433 return netdev::getAttrs(mZone.getInitPid(), netdev);
436 std::vector<std::string> Zone::getNetdevList()
438 Lock lock(mReconnectMutex);
439 return netdev::listNetdev(mZone.getInitPid());
442 void Zone::deleteNetdevIpAddress(const std::string& netdev, const std::string& ip)
444 Lock lock(mReconnectMutex);
445 netdev::deleteIpAddress(mZone.getInitPid(), netdev, ip);
448 std::int64_t Zone::getSchedulerQuota()
451 if (!lxc::getCgroup(mId, "cpu", "cpu.cfs_quota_us", ret)) {
452 LOGE(mId << ": Error while getting the zone's scheduler quota param");
453 throw ZoneOperationException("Could not get scheduler quota param");
455 return std::stoll(ret);
458 void Zone::setSchedulerLevel(SchedulerLevel sched)
463 case SchedulerLevel::FOREGROUND:
464 LOGD(mId << ": Setting SchedulerLevel::FOREGROUND");
465 setSchedulerParams(DEFAULT_CPU_SHARES,
466 DEFAULT_VCPU_PERIOD_MS,
467 mConfig.cpuQuotaForeground);
469 case SchedulerLevel::BACKGROUND:
470 LOGD(mId << ": Setting SchedulerLevel::BACKGROUND");
471 setSchedulerParams(DEFAULT_CPU_SHARES,
472 DEFAULT_VCPU_PERIOD_MS,
473 mConfig.cpuQuotaBackground);
476 assert(0 && "Unknown sched parameter value");
480 void Zone::setSchedulerParams(std::uint64_t cpuShares,
481 std::uint64_t vcpuPeriod,
482 std::int64_t vcpuQuota)
484 assert(vcpuPeriod >= 1000 && vcpuPeriod <= 1000000);
485 assert(vcpuQuota == -1 ||
486 (vcpuQuota >= 1000 && vcpuQuota <= static_cast<std::int64_t>(ULLONG_MAX / 1000)));
488 if (!lxc::setCgroup(mId, "cpu", "cpu.shares", std::to_string(cpuShares)) ||
489 !lxc::setCgroup(mId, "cpu", "cpu.cfs_period_us", std::to_string(vcpuPeriod)) ||
490 !lxc::setCgroup(mId, "cpu", "cpu.cfs_quota_us", std::to_string(vcpuQuota))) {
492 LOGE(mId << ": Error while setting the zone's scheduler params");
493 throw ZoneOperationException("Could not set scheduler params");