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 throw ZoneOperationException("Could not start zone");
210 // Wait until the full platform launch with graphical stack.
211 // VT should be activated by a graphical stack.
212 // If we do it with 'zoneToFocus.activateVT' before starting the graphical stack,
213 // graphical stack initialization failed and we finally switch to the black screen.
214 // Skip waiting when graphical stack is not running (unit tests).
215 if (mDynamicConfig.vt > 0) {
216 // TODO, timeout is a temporary solution
217 std::this_thread::sleep_for(std::chrono::milliseconds(4000));
220 LOGD(mId << ": Started");
222 // Increase cpu quota before connect, otherwise it'd take ages.
224 // refocus in ZonesManager will adjust cpu quota after all
227 void Zone::stop(bool saveState)
229 Lock lock(mReconnectMutex);
231 LOGD(mId << ": Stopping procedure started...");
234 updateRequestedState(STATE_STOPPED);
242 LOGD(mId << ": Already crashed/down/off - nothing to do");
246 if (!mZone.shutdown(mConfig.shutdownTimeout)) {
249 throw ZoneOperationException("Could not stop zone");
255 LOGD(mId << ": Stopping procedure ended");
258 int Zone::getVT() const
260 Lock lock(mReconnectMutex);
261 return mDynamicConfig.vt;
264 std::string Zone::getRootPath() const
269 bool Zone::activateVT()
271 Lock lock(mReconnectMutex);
273 if (mDynamicConfig.vt >= 0) {
274 return utils::activateVT(mDynamicConfig.vt);
280 void Zone::createNetdevVeth(const std::string& zoneDev,
281 const std::string& hostDev)
283 Lock lock(mReconnectMutex);
284 netdev::createVeth(mZone.getInitPid(), zoneDev, hostDev);
287 void Zone::createNetdevMacvlan(const std::string& zoneDev,
288 const std::string& hostDev,
289 const uint32_t& mode)
291 Lock lock(mReconnectMutex);
292 netdev::createMacvlan(mZone.getInitPid(), zoneDev, hostDev, static_cast<macvlan_mode>(mode));
295 void Zone::moveNetdev(const std::string& devId)
297 Lock lock(mReconnectMutex);
298 netdev::movePhys(mZone.getInitPid(), devId);
301 void Zone::destroyNetdev(const std::string& devId)
303 Lock lock(mReconnectMutex);
304 netdev::destroyNetdev(devId, mZone.getInitPid());
307 void Zone::goForeground()
309 Lock lock(mReconnectMutex);
310 setSchedulerLevel(SchedulerLevel::FOREGROUND);
313 void Zone::goBackground()
315 Lock lock(mReconnectMutex);
316 setSchedulerLevel(SchedulerLevel::BACKGROUND);
319 void Zone::setDetachOnExit()
321 Lock lock(mReconnectMutex);
322 mDetachOnExit = true;
325 void Zone::setDestroyOnExit()
327 Lock lock(mReconnectMutex);
328 mDestroyOnExit = true;
331 bool Zone::isRunning()
333 Lock lock(mReconnectMutex);
334 return mZone.getState() == lxc::LxcZone::State::RUNNING;
337 bool Zone::isStopped()
339 Lock lock(mReconnectMutex);
340 return mZone.getState() == lxc::LxcZone::State::STOPPED;
345 Lock lock(mReconnectMutex);
347 LOGD(mId << ": Pausing...");
348 if (!mZone.freeze()) {
349 throw ZoneOperationException("Could not pause zone");
351 LOGD(mId << ": Paused");
353 updateRequestedState(STATE_PAUSED);
358 Lock lock(mReconnectMutex);
360 LOGD(mId << ": Resuming...");
361 if (!mZone.unfreeze()) {
362 throw ZoneOperationException("Could not resume zone");
364 LOGD(mId << ": Resumed");
366 updateRequestedState(STATE_RUNNING);
369 bool Zone::isPaused()
371 Lock lock(mReconnectMutex);
372 return mZone.getState() == lxc::LxcZone::State::FROZEN;
375 bool Zone::isSwitchToDefaultAfterTimeoutAllowed() const
377 return mConfig.switchToDefaultAfterTimeout;
380 int Zone::createFile(const std::string& path, const std::int32_t flags, const std::int32_t mode)
383 if (!mZone.createFile(path, flags, mode, &fd)) {
384 throw ZoneOperationException("Failed to create file.");
389 std::string Zone::declareFile(const int32_t& type,
390 const std::string& path,
391 const int32_t& flags,
394 return mProvision->declareFile(type, path, flags, mode);
397 std::string Zone::declareMount(const std::string& source,
398 const std::string& target,
399 const std::string& type,
400 const int64_t& flags,
401 const std::string& data)
403 return mProvision->declareMount(source, target, type, flags, data);
406 std::string Zone::declareLink(const std::string& source,
407 const std::string& target)
409 return mProvision->declareLink(source, target);
412 std::vector<std::string> Zone::getDeclarations() const
414 return mProvision->list();
417 void Zone::removeDeclaration(const std::string& declarationId)
419 mProvision->remove(declarationId);
422 void Zone::setNetdevAttrs(const std::string& netdev, const NetdevAttrs& attrs)
424 Lock lock(mReconnectMutex);
425 netdev::setAttrs(mZone.getInitPid(), netdev, attrs);
428 Zone::NetdevAttrs Zone::getNetdevAttrs(const std::string& netdev)
430 Lock lock(mReconnectMutex);
431 return netdev::getAttrs(mZone.getInitPid(), netdev);
434 std::vector<std::string> Zone::getNetdevList()
436 Lock lock(mReconnectMutex);
437 return netdev::listNetdev(mZone.getInitPid());
440 void Zone::deleteNetdevIpAddress(const std::string& netdev, const std::string& ip)
442 Lock lock(mReconnectMutex);
443 netdev::deleteIpAddress(mZone.getInitPid(), netdev, ip);
446 std::int64_t Zone::getSchedulerQuota()
449 if (!lxc::getCgroup(mId, "cpu", "cpu.cfs_quota_us", ret)) {
450 LOGE(mId << ": Error while getting the zone's scheduler quota param");
451 throw ZoneOperationException("Could not get scheduler quota param");
453 return std::stoll(ret);
456 void Zone::setSchedulerLevel(SchedulerLevel sched)
461 case SchedulerLevel::FOREGROUND:
462 LOGD(mId << ": Setting SchedulerLevel::FOREGROUND");
463 setSchedulerParams(DEFAULT_CPU_SHARES,
464 DEFAULT_VCPU_PERIOD_MS,
465 mConfig.cpuQuotaForeground);
467 case SchedulerLevel::BACKGROUND:
468 LOGD(mId << ": Setting SchedulerLevel::BACKGROUND");
469 setSchedulerParams(DEFAULT_CPU_SHARES,
470 DEFAULT_VCPU_PERIOD_MS,
471 mConfig.cpuQuotaBackground);
474 assert(0 && "Unknown sched parameter value");
478 void Zone::setSchedulerParams(std::uint64_t cpuShares,
479 std::uint64_t vcpuPeriod,
480 std::int64_t vcpuQuota)
482 assert(vcpuPeriod >= 1000 && vcpuPeriod <= 1000000);
483 assert(vcpuQuota == -1 ||
484 (vcpuQuota >= 1000 && vcpuQuota <= static_cast<std::int64_t>(ULLONG_MAX / 1000)));
486 if (!lxc::setCgroup(mId, "cpu", "cpu.shares", std::to_string(cpuShares)) ||
487 !lxc::setCgroup(mId, "cpu", "cpu.cfs_period_us", std::to_string(vcpuPeriod)) ||
488 !lxc::setCgroup(mId, "cpu", "cpu.cfs_quota_us", std::to_string(vcpuQuota))) {
490 LOGE(mId << ": Error while setting the zone's scheduler params");
491 throw ZoneOperationException("Could not set scheduler params");