blob: c242bcc29c490841354d6fdc8d0de16eeea602ed [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 __RESOURCES_HPP__
#define __RESOURCES_HPP__
#include <iostream>
#include <string>
#include <vector>
#include <mesos/mesos.hpp>
#include <mesos/values.hpp>
#include <stout/bytes.hpp>
#include <stout/check.hpp>
#include <stout/error.hpp>
#include <stout/foreach.hpp>
#include <stout/option.hpp>
#include <stout/try.hpp>
// Resources come in three types: scalar, ranges, and sets. These are
// represented using protocol buffers. To make manipulation of
// resources easier within the Mesos core and for scheduler writers,
// we provide generic overloaded operators (see below) as well as a
// general Resources class that encapsulates a collection of protocol
// buffer Resource objects. The Resources class also provides a few
// static routines to allow parsing resources (e.g., from the command
// line), as well as determining whether or not a Resource object is
// valid. Note that many of these operations have not been optimized
// but instead just written for correct semantics.
namespace mesos {
// NOTE: Resource objects stored in the class are always valid and
// kept combined if possible. It is the caller's responsibility to
// validate any Resource object or repeated Resource protobufs before
// constructing a Resources object. Otherwise, invalid Resource
// objects will be silently stripped. Invalid Resource objects will
// also be silently ignored when used in arithmetic operations (e.g.,
// +=, -=, etc.).
class Resources
{
public:
// Parses the text and returns a Resource object with the given name
// and role. For example, "Resource r = parse("mem", "1024", "*");".
static Try<Resource> parse(
const std::string& name,
const std::string& value,
const std::string& role);
// Parses Resources from text in the form "name:value(role);
// name:value;...". Any name/value pair that doesn't specify a role
// is assigned to defaultRole.
static Try<Resources> parse(
const std::string& text,
const std::string& defaultRole = "*");
// Validates the given Resource object. Returns Error if it is not
// valid. A Resource object is valid if it has a name, a valid type,
// i.e. scalar, range, or set, and has the appropriate value set.
static Option<Error> validate(const Resource& resource);
// Validates the given protobufs.
// TODO(jieyu): Right now, it's the same as checking each individual
// Resource object in the protobufs. In the future, we could add
// more checks that are not possible if checking each Resource
// object individually. For example, we could check multiple usage
// of an item in a set or a ranges, etc.
static Option<Error> validate(
const google::protobuf::RepeatedPtrField<Resource>& resources);
// NOTE: The following predicate functions assume that the given
// resource is validated.
// Tests if the given Resource object is empty.
static bool isEmpty(const Resource& resource);
// Tests if the given Resource object is a persistent volume.
static bool isPersistentVolume(const Resource& resource);
// Tests if the given Resource object is reserved.
static bool isReserved(const Resource& resource);
// Tests if the given Resource object is reserved for the given role.
static bool isReserved(const Resource& resource, const std::string& role);
// Tests if the given Resource object is unreserved.
static bool isUnreserved(const Resource& resource);
Resources() {}
// TODO(jieyu): Consider using C++11 initializer list.
/*implicit*/ Resources(const Resource& resource);
/*implicit*/
Resources(const std::vector<Resource>& _resources);
/*implicit*/
Resources(const google::protobuf::RepeatedPtrField<Resource>& _resources);
Resources(const Resources& that) : resources(that.resources) {}
Resources& operator = (const Resources& that)
{
if (this != &that) {
resources = that.resources;
}
return *this;
}
bool empty() const { return resources.size() == 0; }
// Checks if this Resources is a superset of the given Resources.
bool contains(const Resources& that) const;
// Checks if this Resources contains the given Resource.
bool contains(const Resource& that) const;
// Returns the reserved resources, by role.
hashmap<std::string, Resources> reserved() const;
// Returns the reserved resources for the role. Note that the "*"
// role represents unreserved resources, and will be ignored.
Resources reserved(const std::string& role) const;
// Returns the unreserved resources.
Resources unreserved() const;
// Returns a Resources object with the same amount of each resource
// type as these Resources, but with all Resource objects marked as
// the specified role.
Resources flatten(const std::string& role = "*") const;
// Finds a Resources object with the same amount of each resource
// type as "targets" from these Resources. The roles specified in
// "targets" set the preference order. For each resource type,
// resources are first taken from the specified role, then from '*',
// then from any other role.
// TODO(jieyu): 'find' contains some allocation logic for scalars and
// fixed set / range elements. However, this is not sufficient for
// schedulers that want, say, any N available ports. We should
// consider moving this to an internal "allocation" library for our
// example frameworks to leverage.
Option<Resources> find(const Resources& targets) const;
// Certain offer operations (e.g., RESERVE, UNRESERVE, CREATE or
// DESTROY) alter the offered resources. The following methods
// provide a convenient way to get the transformed resources by
// applying the given offer operation(s). Returns an Error if the
// offer operation(s) cannot be applied.
Try<Resources> apply(const Offer::Operation& operation) const;
template <typename Iterable>
Try<Resources> apply(const Iterable& operations) const
{
Resources result = *this;
foreach (const Offer::Operation& operation, operations) {
Try<Resources> transformed = result.apply(operation);
if (transformed.isError()) {
return Error(transformed.error());
}
result = transformed.get();
}
return result;
}
// Helpers to get resource values. We consider all roles here.
template <typename T>
Option<T> get(const std::string& name) const;
// Helpers to get known resource types.
// TODO(vinod): Fix this when we make these types as first class
// protobufs.
Option<double> cpus() const;
Option<Bytes> mem() const;
Option<Bytes> disk() const;
// TODO(vinod): Provide a Ranges abstraction.
Option<Value::Ranges> ports() const;
// TODO(jieyu): Consider returning an EphemeralPorts abstraction
// which holds the ephemeral ports allocation logic.
Option<Value::Ranges> ephemeral_ports() const;
typedef google::protobuf::RepeatedPtrField<Resource>::iterator
iterator;
typedef google::protobuf::RepeatedPtrField<Resource>::const_iterator
const_iterator;
iterator begin() { return resources.begin(); }
iterator end() { return resources.end(); }
const_iterator begin() const { return resources.begin(); }
const_iterator end() const { return resources.end(); }
// Using this operator makes it easy to copy a resources object into
// a protocol buffer field.
operator const google::protobuf::RepeatedPtrField<Resource>& () const;
bool operator == (const Resources& that) const;
bool operator != (const Resources& that) const;
// NOTE: If any error occurs (e.g., input Resource is not valid or
// the first operand is not a superset of the second oprand while
// doing subtraction), the semantics is as though the second operand
// was actually just an empty resource (as though you didn't do the
// operation at all).
Resources operator + (const Resource& that) const;
Resources operator + (const Resources& that) const;
Resources& operator += (const Resource& that);
Resources& operator += (const Resources& that);
Resources operator - (const Resource& that) const;
Resources operator - (const Resources& that) const;
Resources& operator -= (const Resource& that);
Resources& operator -= (const Resources& that);
// The base class for all resources filters.
// TODO(jieyu): Pull resources filters out of Resources class and
// possibly put them inside a resources::filter namespace.
class Filter
{
public:
// Apply this filter to the given resources and return the
// filtered resources.
virtual Resources apply(const Resources& resources) const = 0;
};
class RoleFilter : public Filter
{
public:
static RoleFilter any() { return RoleFilter(); }
RoleFilter() : type(ANY) {}
explicit RoleFilter(const std::string& _role)
: type(SOME), role(_role) {}
virtual Resources apply(const Resources& resources) const
{
if (type == ANY) {
return resources;
}
CHECK_SOME(role);
return role.get() == "*" ?
resources.unreserved() :
resources.reserved(role.get());
}
private:
enum { ANY, SOME } type;
Option<std::string> role;
};
class PersistentVolumeFilter : public Filter
{
public:
PersistentVolumeFilter() {}
virtual Resources apply(const Resources& resources) const
{
Resources result;
foreach (const Resource& resource, resources) {
if (isPersistentVolume(resource)) {
result += resource;
}
}
return result;
}
};
// Resources that need checkpointing on the slave.
// TODO(jieyu): This filter is only used by master and slave.
// Consider pulling this out of this header.
class CheckpointFilter : public Filter
{
public:
CheckpointFilter() {}
virtual Resources apply(const Resources& resources) const
{
Resources result;
foreach (const Resource& resource, resources) {
// TODO(jieyu): Consider dynamic reservation as well.
if (isPersistentVolume(resource)) {
result += resource;
}
}
return result;
}
};
private:
// Similar to 'contains(const Resource&)' but skips the validity
// check. This can be used to avoid the performance overhead of
// calling 'contains(const Resource&)' when the resource can be
// assumed valid (e.g. it's inside a Resources).
//
// TODO(jieyu): Measure performance overhead of validity check to
// ensure this is warranted.
bool _contains(const Resource& that) const;
// Similar to the public 'find', but only for a single Resource
// object. The target resource may span multiple roles, so this
// returns Resources.
Option<Resources> find(const Resource& target) const;
google::protobuf::RepeatedPtrField<Resource> resources;
};
std::ostream& operator << (std::ostream& stream, const Resource& resource);
std::ostream& operator << (std::ostream& stream, const Resources& resources);
inline std::ostream& operator << (
std::ostream& stream,
const google::protobuf::RepeatedPtrField<Resource>& resources)
{
return stream << Resources(resources);
}
inline Resources operator + (
const google::protobuf::RepeatedPtrField<Resource>& left,
const Resources& right)
{
return Resources(left) + right;
}
inline Resources operator - (
const google::protobuf::RepeatedPtrField<Resource>& left,
const Resources& right)
{
return Resources(left) - right;
}
inline bool operator == (
const google::protobuf::RepeatedPtrField<Resource>& left,
const Resources& right)
{
return Resources(left) == right;
}
} // namespace mesos {
#endif // __RESOURCES_HPP__