// Copyright 2016 Twitter. All rights reserved.
//
// Licensed 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.
package com.twitter.heron.scheduler.mesos.framework;
import java.util.ArrayList;
import java.util.List;
import org.apache.mesos.Protos;
/**
* TaskResources is an utils class for easy access of Protos.Offer
*/
public class TaskResources {
public static final String CPUS_RESOURCE_NAME = "cpus";
public static final String MEM_RESOURCE_NAME = "mem";
public static final String DISK_RESOURCE_NAME = "disk";
public static final String PORT_RESOURCE_NAME = "ports";
public static class Range {
public long rangeStart;
public long rangeEnd;
public Range(long rangeStart, long rangeEnd) {
this.rangeStart = rangeStart;
this.rangeEnd = rangeEnd;
}
}
private double cpu;
private double mem;
private double disk;
// # of ports request
private int ports;
private List<Range> portsHold = new ArrayList<>();
public TaskResources(double cpu, double mem, double disk, List<Range> portsResources) {
this.cpu = cpu;
this.mem = mem;
this.disk = disk;
this.portsHold = portsResources;
for (Range r : portsResources) {
this.ports += r.rangeEnd - r.rangeStart + 1;
}
}
public TaskResources(double cpu, double mem, double disk, int ports) {
this.cpu = cpu;
this.mem = mem;
this.disk = disk;
this.ports = ports;
// No specific ports value needed. So portsHold is empty
}
// Whether this resource can satisfy the TaskResources needed from parameter
public boolean canSatisfy(TaskResources needed) {
return this.ports >= needed.ports
&& (this.cpu >= needed.cpu)
&& (this.mem >= needed.mem)
&& (this.disk >= needed.disk);
}
public void consume(TaskResources needed) {
this.cpu -= needed.cpu;
this.mem -= needed.mem;
this.disk -= needed.disk;
this.ports -= needed.ports;
// Consume the port
for (Range portRange : portsHold) {
if (portRange.rangeEnd - portRange.rangeStart + 1 > needed.ports) {
long rangeEnd = portRange.rangeStart + needed.ports - 1;
needed.portsHold.add(new Range(portRange.rangeStart, rangeEnd));
// And then consume us by update the range value
portRange.rangeStart = portRange.rangeStart + needed.ports;
// Consumptino done. Break the loop
break;
}
}
}
public List<Range> getPortsHold() {
return portsHold;
}
public double getCpu() {
return cpu;
}
public double getMem() {
return mem;
}
public double getDisk() {
return disk;
}
public int getPorts() {
return ports;
}
public String toString() {
return String.format("cpu: %f; mem: %f; disk: %f; ports: %d.",
this.cpu, this.mem, this.disk, this.ports);
}
// A static method to construct a TaskResources from mesos Protos.Offer
public static TaskResources apply(Protos.Offer offer, String role) {
double cpu = 0;
double mem = 0;
double disk = 0;
List<Range> portsResource = new ArrayList<>();
for (Protos.Resource r : offer.getResourcesList()) {
if (!r.hasRole() || r.getRole().equals("*") || r.getRole().equals(role)) {
if (r.getName().equals(CPUS_RESOURCE_NAME)) {
cpu = r.getScalar().getValue();
}
if (r.getName().equals(MEM_RESOURCE_NAME)) {
mem = r.getScalar().getValue();
}
if (r.getName().equals(DISK_RESOURCE_NAME)) {
disk = r.getScalar().getValue();
}
if (r.getName().equals(PORT_RESOURCE_NAME)) {
Protos.Value.Ranges ranges = r.getRanges();
for (Protos.Value.Range range : ranges.getRangeList()) {
portsResource.add(new Range(range.getBegin(), range.getEnd()));
}
}
}
}
return new TaskResources(cpu, mem, disk, portsResource);
}
}