blob: 59f495cdb2a2d55a6c436767789ee579cd5c2f97 [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 <iterator>
#include <string>
#include <utility>
#include <vector>
#include <mesos/mesos.hpp>
#include <mesos/values.hpp>
#include <stout/bytes.hpp>
#include <stout/foreach.hpp>
#include <stout/none.hpp>
#include <stout/option.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 opertors (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 or allocatable. Note that many of these operations have not
* been optimized but instead just written for correct semantics.
*
* Note! A resource is described by a tuple (name, type, role). Doing
* "arithmetic" operations (those defined below) on two resources of
* the same name but different type, or the same name and type but
* different roles, doesn't make sense, so it's semantics are as
* though the second operand was actually just an empty resource
* (as though you didn't do the operation at all). In addition,
* doing operations on two resources of the same type but different
* names is a no-op.
*/
namespace mesos {
bool operator == (const Resource& left, const Resource& right);
bool operator != (const Resource& left, const Resource& right);
bool operator <= (const Resource& left, const Resource& right);
Resource operator + (const Resource& left, const Resource& right);
Resource operator - (const Resource& left, const Resource& right);
Resource& operator += (Resource& left, const Resource& right);
Resource& operator -= (Resource& left, const Resource& right);
// Return true iff both Resources have the same name, type, and role.
bool matches(const Resource& left, const Resource& right);
std::ostream& operator << (std::ostream& stream, const Resource& resource);
class Resources
{
public:
Resources() {}
Resources(const google::protobuf::RepeatedPtrField<Resource>& _resources)
{
resources.MergeFrom(_resources);
}
Resources(const Resources& that)
{
resources.MergeFrom(that.resources);
}
Resources& operator = (const Resources& that)
{
if (this != &that) {
resources.Clear();
resources.MergeFrom(that.resources);
}
return *this;
}
/**
* Returns a Resources object with only the allocatable resources.
*/
Resources allocatable() const
{
Resources result;
foreach (const Resource& resource, resources) {
if (isAllocatable(resource)) {
result.resources.Add()->MergeFrom(resource);
}
}
return result;
}
size_t size() const
{
return resources.size();
}
/**
* Using this operator makes it easy to copy a resources object into
* a protocol buffer field.
*/
operator const google::protobuf::RepeatedPtrField<Resource>& () const
{
return resources;
}
bool operator == (const Resources& that) const
{
if (size() != that.size()) {
return false;
}
foreach (const Resource& resource, resources) {
Option<Resource> option = that.get(resource);
if (option.isNone()) {
return false;
} else {
if (!(resource == option.get())) {
return false;
}
}
}
return true;
}
bool operator != (const Resources& that) const
{
return !(*this == that);
}
bool operator <= (const Resources& that) const
{
foreach (const Resource& resource, resources) {
Option<Resource> option = that.get(resource);
if (option.isNone()) {
return false;
} else {
if (!(resource <= option.get())) {
return false;
}
}
}
return true;
}
Resources operator + (const Resources& that) const
{
Resources result(*this);
foreach (const Resource& resource, that.resources) {
result += resource;
}
return result;
}
Resources operator - (const Resources& that) const
{
Resources result(*this);
foreach (const Resource& resource, that.resources) {
result -= resource;
}
return result;
}
Resources& operator += (const Resources& that)
{
foreach (const Resource& resource, that.resources) {
*this += resource;
}
return *this;
}
Resources& operator -= (const Resources& that)
{
foreach (const Resource& resource, that.resources) {
*this -= resource;
}
return *this;
}
Resources operator + (const Resource& that) const
{
Resources result;
bool added = false;
foreach (const Resource& resource, resources) {
if (matches(resource, that)) {
result.resources.Add()->MergeFrom(resource + that);
added = true;
} else {
result.resources.Add()->MergeFrom(resource);
}
}
if (!added) {
result.resources.Add()->MergeFrom(that);
}
return result;
}
Resources operator - (const Resource& that) const
{
Resources result;
foreach (const Resource& resource, resources) {
if (matches(resource, that)) {
Resource r = resource - that;
if (!isZero(r)) {
result.resources.Add()->MergeFrom(r);
}
} else {
result.resources.Add()->MergeFrom(resource);
}
}
return result;
}
Resources& operator += (const Resource& that)
{
*this = *this + that;
return *this;
}
Resources& operator -= (const Resource& that)
{
*this = *this - that;
return *this;
}
/**
* Returns a Resources object with the same amount of each resource
* type as these Resources, but with only one Resource object per
* type and all Resource object marked as the specified role.
*/
Resources flatten(const std::string& role = "*") const;
/**
* Returns all resources in this object that are marked with the
* specified role.
*/
Resources extract(const std::string& role) const;
/**
* Finds a number of resources equal to toFind in these Resources
* and returns them marked with appropriate roles. For each resource
* type, resources are first taken from the specified role, then
* from '*', then from any other role.
*/
Option<Resources> find(
const Resources& toFind,
const std::string& role = "*") const;
/**
* Returns the Resource from these Resources that matches the argument
* in name, type, and role, if it exists.
*/
Option<Resource> get(const Resource& r) const;
/**
* Returns all Resources from these Resources that match the argument
* in name and type, regardless of role.
*/
Option<Resources> getAll(const Resource& r) const;
template <typename T>
T get(const std::string& name, const T& t) 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;
Option<Value::Ranges> ports() const; // TODO(vinod): Provide a Ranges abstraction.
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(); }
/**
* Parses the value and returns a Resource with the given name and role.
*/
static Try<Resource> parse(
const std::string& name,
const std::string& value,
const std::string& role);
/**
* Parses resources 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& s,
const std::string& defaultRole = "*");
/**
* Returns true iff this resource has a name, a valid type, i.e. scalar,
* range, or set, and has the appropriate value set for its type.
*/
static bool isValid(const Resource& resource);
/**
* Returns true iff this resource is valid and allocatable. In particular,
* a scalar is allocatable if it's value is greater than zero, a ranges
* is allocatable if there is at least one valid range in it, and a set
* is allocatable if it has at least one item.
*/
static bool isAllocatable(const Resource& resource);
/**
* Returns true iff this resource is zero valued, i.e. is zero for scalars,
* has a range size of zero for ranges, and has no items for sets.
*/
static bool isZero(const Resource& resource);
private:
google::protobuf::RepeatedPtrField<Resource> resources;
};
template <>
inline Value::Scalar Resources::get(
const std::string& name,
const Value::Scalar& scalar) const
{
Value::Scalar total;
bool found = false;
foreach (const Resource& resource, resources) {
if (resource.name() == name &&
resource.type() == Value::SCALAR) {
total += resource.scalar();
found = true;
}
}
if (found) {
return total;
}
return scalar;
}
template <>
inline Value::Ranges Resources::get(
const std::string& name,
const Value::Ranges& ranges) const
{
Value::Ranges total;
bool found = false;
foreach (const Resource& resource, resources) {
if (resource.name() == name &&
resource.type() == Value::RANGES) {
total += resource.ranges();
found = true;
}
}
if (found) {
return total;
}
return ranges;
}
template <>
inline Value::Set Resources::get(
const std::string& name,
const Value::Set& set) const
{
Value::Set total;
bool found = false;
foreach (const Resource& resource, resources) {
if (resource.name() == name &&
resource.type() == Value::SET) {
total += resource.set();
found = true;
}
}
if (found) {
return total;
}
return set;
}
inline std::ostream& operator << (
std::ostream& stream,
const Resources& resources)
{
mesos::Resources::const_iterator it = resources.begin();
while (it != resources.end()) {
stream << *it;
if (++it != resources.end()) {
stream << "; ";
}
}
return stream;
}
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__