blob: e86062e9abaaa263c32c55e9dbfefd700f605886 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#ifndef __CGROUPS_ISOLATOR_HPP__
#define __CGROUPS_ISOLATOR_HPP__
#include <unistd.h>
#include <map>
#include <sstream>
#include <string>
#include <process/future.hpp>
#include <process/pid.hpp>
#include <stout/hashmap.hpp>
#include <stout/hashset.hpp>
#include <stout/lambda.hpp>
#include <stout/nothing.hpp>
#include <stout/option.hpp>
#include <stout/path.hpp>
#include <stout/proc.hpp>
#include <stout/uuid.hpp>
#include "launcher/launcher.hpp"
#include "slave/flags.hpp"
#include "slave/isolator.hpp"
#include "slave/reaper.hpp"
#include "slave/slave.hpp"
namespace mesos {
namespace internal {
namespace slave {
// TODO(bmahler): Migrate this into it's own file, along with moving
// all cgroups code inside of a 'cgroups' directory.
class Cpuset
{
public:
// Grows this cpu set by the provided delta.
// @param delta Amount of cpus to grow by.
// @param usage Cpu usage, as allocated by the cgroups isolator.
// @return The new cpu allocations made by this Cpuset.
std::map<proc::CPU, double> grow(
double delta,
const std::map<proc::CPU, double>& usage);
// Shrinks this cpu set by the provided delta.
// @param delta Amount of cpus to shrink by.
// @return The new cpu deallocations made by this Cpuset.
std::map<proc::CPU, double> shrink(double delta);
// @return The total cpu usage across all the cpus in this Cpuset.
double usage() const;
friend std::ostream& operator << (std::ostream& out, const Cpuset& cpuset);
private:
std::map<proc::CPU, double> cpus; // CPU id -> % allocated.
};
class CgroupsIsolator : public Isolator
{
public:
CgroupsIsolator();
virtual void initialize(
const Flags& flags,
const Resources& resources,
bool local,
const process::PID<Slave>& slave);
virtual void finalize();
virtual void launchExecutor(
const SlaveID& slaveId,
const FrameworkID& frameworkId,
const FrameworkInfo& frameworkInfo,
const ExecutorInfo& executorInfo,
const UUID& uuid,
const std::string& directory,
const Resources& resources);
virtual void killExecutor(
const FrameworkID& frameworkId,
const ExecutorID& executorId);
virtual void resourcesChanged(
const FrameworkID& frameworkId,
const ExecutorID& executorId,
const Resources& resources);
virtual process::Future<ResourceStatistics> usage(
const FrameworkID& frameworkId,
const ExecutorID& executorId);
virtual process::Future<Nothing> recover(
const Option<state::SlaveState>& state);
private:
// No copying, no assigning.
CgroupsIsolator(const CgroupsIsolator&);
CgroupsIsolator& operator = (const CgroupsIsolator&);
void reaped(pid_t pid, const Future<Option<int> >& status);
// The cgroup information for each live executor.
struct CgroupInfo
{
~CgroupInfo()
{
if (cpuset != NULL) {
delete cpuset;
cpuset = NULL;
}
}
// Returns the canonicalized name of the cgroup in the filesystem.
std::string name() const
{
CHECK_SOME(uuid);
std::ostringstream out;
out << "framework_" << frameworkId
<< "_executor_" << executorId
<< "_tag_" << uuid.get();
return path::join(flags.cgroups_root, out.str());
}
FrameworkID frameworkId;
ExecutorID executorId;
// The UUID to distinguish between different launches of the same
// executor (which have the same frameworkId and executorId).
Option<UUID> uuid;
// PID of the forked process of the executor.
Option<pid_t> pid;
bool killed; // True if "killing" has been initiated via 'killExecutor()'.
// Indicates if this executor has been destroyed by the isolator.
// NOTE: An executor may have terminated due to reasons
// other than destruction by the isolator (e.g. killed by
// slave, exited, etc.).
bool destroyed;
std::string message; // The reason behind the destruction.
Option<int> status; // Exit status of the executor.
Flags flags; // Slave flags.
Resources resources; // Resources allocated to the cgroup.
// Used to cancel the OOM listening.
process::Future<uint64_t> oomNotifier;
// CPUs allocated if using 'cpuset' subsystem.
Cpuset* cpuset;
};
// The callback which will be invoked when "cpus" resource has changed.
// @param info The Cgroup information.
// @param resources The handle for the resources.
// @return Whether the operation succeeds.
Try<Nothing> cpusChanged(
CgroupInfo* info,
const Resource& resource);
// The callback which will be invoked when "cpus" resource has changed.
// This is only invoked when we are using the cpuset subsystem.
// @param info The Cgroup information.
// @param resources The handle for the resources.
// @return Whether the operation succeeds.
Try<Nothing> cpusetChanged(
CgroupInfo* info,
const Resource& resource);
// The callback which will be invoked when "cpus" resource has changed,
// and the cfs cgroups feature flag is enabled..
// @param info The Cgroup information.
// @param resources The handle for the resources.
// @return Whether the operation succeeds.
Try<Nothing> cfsChanged(
CgroupInfo* info,
const Resource& resource);
// The callback which will be invoked when "mem" resource has changed.
// @param info The Cgroup information.
// @param resources The handle for the resources.
// @return Whether the operation succeeds.
Try<Nothing> memChanged(
CgroupInfo* info,
const Resource& resource);
// Start listening on OOM events. This function will create an eventfd and
// start polling on it.
// @param frameworkId The id of the given framework.
// @param executorId The id of the given executor.
void oomListen(
const FrameworkID& frameworkId,
const ExecutorID& executorId);
// This function is invoked when the polling on eventfd has a result.
// @param frameworkId The id of the given framework.
// @param executorId The id of the given executor.
// @param uuid The uuid of the given executor.
void oomWaited(
const FrameworkID& frameworkId,
const ExecutorID& executorId,
const UUID& uuid,
const process::Future<uint64_t>& future);
// This function is invoked when the OOM event happens.
// @param frameworkId The id of the given framework.
// @param executorId The id of the given executor.
// @param uuid The uuid of the given executor.
void oom(
const FrameworkID& frameworkId,
const ExecutorID& executorId,
const UUID& uuid);
// This callback is invoked when destroy cgroup has a result.
// @param info The information of cgroup that is being destroyed.
// @param future The future describing the destroy process.
void _killExecutor(
CgroupInfo* info,
const process::Future<bool>& future);
// This callback is invoked when destroying orphaned cgroups from the
// previous slave execution.
// @param cgroup The cgroup that is being destroyed.
// @param future The future describing the destroy process.
void _destroy(
const std::string& cgroup,
const process::Future<bool>& future);
// Register a cgroup in the isolator.
// @param frameworkId The id of the given framework.
// @param executorId The id of the given executor.
// @param uuid The uuid of the given executor run.
// @param pid The executor pid.
// @param flags The slave flags.
// @return A pointer to the cgroup info registered.
CgroupInfo* registerCgroupInfo(
const FrameworkID& frameworkId,
const ExecutorID& executorId,
const UUID& uuid,
const Option<pid_t>& pid,
const Flags& flags);
// Unregister a cgroup in the isolator.
// @param frameworkId The id of the given framework.
// @param executorId The id of the given executor.
void unregisterCgroupInfo(
const FrameworkID& frameworkId,
const ExecutorID& executorId);
// Find a registered cgroup by the PID of the leading process.
// @param pid The PID of the leading process in the cgroup.
// @return A pointer to the cgroup info if found, NULL otherwise.
CgroupInfo* findCgroupInfo(pid_t pid);
// Find a registered cgroup by the frameworkId and the executorId.
// @param frameworkId The id of the given framework.
// @param executorId The id of the given executor.
// @return A pointer to the cgroup info if found, NULL otherwise.
CgroupInfo* findCgroupInfo(
const FrameworkID& frameworkId,
const ExecutorID& executorId);
Flags flags;
bool local;
process::PID<Slave> slave;
bool initialized;
Reaper reaper;
// File descriptor to 'mesos/tasks' file in the cgroup on which we place
// an advisory lock.
Option<int> lockFile;
// The cgroup information for each live executor.
hashmap<FrameworkID, hashmap<ExecutorID, CgroupInfo*> > infos;
// The path to the cgroups hierarchy root.
std::string hierarchy;
// The cgroups subsystems being used.
hashset<std::string> subsystems;
// Allocated cpus (if using cpuset subsystem).
std::map<proc::CPU, double> cpus;
// Handlers for each resource name, used for resource changes.
hashmap<std::string,
Try<Nothing>(CgroupsIsolator::*)(
CgroupInfo*,
const Resource&)> handlers;
};
} // namespace mesos {
} // namespace internal {
} // namespace slave {
#endif // __CGROUPS_ISOLATOR_HPP__