Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.netflix.fenzo.AssignableVMs Maven / Gradle / Ivy
/*
* Copyright 2015 Netflix, Inc.
*
* 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.netflix.fenzo;
import com.netflix.fenzo.functions.Action1;
import com.netflix.fenzo.functions.Func1;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
class AssignableVMs {
static class VMRejectLimiter {
private long lastRejectAt=0;
private int rejectedCount;
private final int limit;
private final long rejectDelay;
VMRejectLimiter(int limit, long leaseOfferExpirySecs) {
this.limit = limit;
this.rejectDelay = leaseOfferExpirySecs*1000L;
}
synchronized boolean reject() {
if(rejectedCount==limit)
return false;
rejectedCount++;
lastRejectAt = System.currentTimeMillis();
return true;
}
boolean limitReached() {
return rejectedCount == limit;
}
private void reset() {
if(System.currentTimeMillis() > (lastRejectAt + rejectDelay))
rejectedCount=0;
}
}
private static class HostDisablePair {
private final String host;
private final Long until;
HostDisablePair(String host, Long until) {
this.host = host;
this.until = until;
}
}
private final VMCollection vmCollection;
private static final Logger logger = LoggerFactory.getLogger(AssignableVMs.class);
private final ConcurrentMap leaseIdToHostnameMap = new ConcurrentHashMap<>();
private final ConcurrentMap vmIdToHostnameMap = new ConcurrentHashMap<>();
private final BlockingQueue disableRequests = new LinkedBlockingQueue<>();
private final TaskTracker taskTracker;
private final String attrNameToGroupMaxResources;
private final Map> maxResourcesMap;
private final Map totalResourcesMap;
private final VMRejectLimiter vmRejectLimiter;
private final AssignableVirtualMachine dummyVM = new AssignableVirtualMachine(null, null, "", null, 0L, null) {
@Override
void assignResult(TaskAssignmentResult result) {
throw new UnsupportedOperationException();
}
};
private final ActiveVmGroups activeVmGroups;
private String activeVmGroupAttributeName=null;
private final List unknownLeaseIdsToExpire = new ArrayList<>();
AssignableVMs(TaskTracker taskTracker, Action1 leaseRejectAction,
long leaseOfferExpirySecs, int maxOffersToReject,
String attrNameToGroupMaxResources, boolean singleLeaseMode, String autoScaleByAttributeName) {
this.taskTracker = taskTracker;
vmCollection = new VMCollection(
hostname -> new AssignableVirtualMachine(vmIdToHostnameMap, leaseIdToHostnameMap, hostname,
leaseRejectAction, leaseOfferExpirySecs, taskTracker, singleLeaseMode),
autoScaleByAttributeName
);
this.attrNameToGroupMaxResources = attrNameToGroupMaxResources;
maxResourcesMap = new HashMap<>();
totalResourcesMap = new HashMap<>();
vmRejectLimiter = new VMRejectLimiter(maxOffersToReject, leaseOfferExpirySecs); // ToDo make this configurable?
activeVmGroups = new ActiveVmGroups();
}
VMCollection getVmCollection() {
return vmCollection;
}
Map> createPseudoHosts(Map groupCounts, Func1 ruleGetter) {
return vmCollection.clonePseudoVMsForGroups(groupCounts, ruleGetter, lease ->
lease != null &&
(lease.getAttributeMap() == null ||
lease.getAttributeMap().get(activeVmGroupAttributeName) == null ||
isInActiveVmGroup(lease.getAttributeMap().get(activeVmGroupAttributeName).getText().getValue())
)
);
}
void removePseudoHosts(Map> hostsMap) {
if (hostsMap != null && !hostsMap.isEmpty()) {
for (Map.Entry> entry: hostsMap.entrySet()) {
for (String h: entry.getValue()) {
final AssignableVirtualMachine avm = vmCollection.unsafeRemoveVm(h, entry.getKey());
if (avm != null)
avm.removeExpiredLeases(true, false);
}
}
}
}
Map> getResourceStatus() {
Map> result = new HashMap<>();
for(AssignableVirtualMachine avm: vmCollection.getAllVMs())
result.put(avm.getHostname(), avm.getResourceStatus());
return result;
}
void setTaskAssigned(TaskRequest request, String host) {
vmCollection.getOrCreate(host).setAssignedTask(request);
}
void unAssignTask(String taskId, String host) {
final Optional vmByName = vmCollection.getVmByName(host);
if(vmByName.isPresent()) {
vmByName.get().markTaskForUnassigning(taskId);
}
else
logger.warn("No VM for host " + host + " to unassign task " + taskId);
}
private int addLeases(List leases) {
if(logger.isDebugEnabled())
logger.debug("Adding leases");
for(AssignableVirtualMachine avm: vmCollection.getAllVMs())
avm.resetResources();
int rejected=0;
for(VirtualMachineLease l: leases) {
if(vmCollection.addLease(l))
rejected++;
}
for(AssignableVirtualMachine avm: vmCollection.getAllVMs()) {
if(logger.isDebugEnabled())
logger.debug("Updating total lease on " + avm.getHostname());
avm.updateCurrTotalLease();
final VirtualMachineLease currTotalLease = avm.getCurrTotalLease();
if(logger.isDebugEnabled()) {
if (currTotalLease == null)
logger.debug("Updated total lease is null for " + avm.getHostname());
else {
logger.debug("Updated total lease for {} has cpu={}, mem={}, disk={}, network={}",
avm.getHostname(), currTotalLease.cpuCores(), currTotalLease.memoryMB(),
currTotalLease.diskMB(), currTotalLease.networkMbps()
);
}
}
}
return rejected;
}
void expireLease(String leaseId) {
final String hostname = leaseIdToHostnameMap.get(leaseId);
if(hostname==null) {
unknownLeaseIdsToExpire.add(leaseId);
return;
}
internalExpireLease(leaseId, hostname);
}
private void internalExpireLease(String leaseId, String hostname) {
final Optional vmByName = vmCollection.getVmByName(hostname);
if(vmByName.isPresent()) {
if(logger.isDebugEnabled())
logger.debug("Expiring lease offer id " + leaseId + " on host " + hostname);
vmByName.get().expireLease(leaseId);
}
}
void expireAllLeases(String hostname) {
final Optional vmByName = vmCollection.getVmByName(hostname);
if(vmByName.isPresent())
vmByName.get().expireAllLeases();
}
void expireAllLeases() {
for(AssignableVirtualMachine avm: vmCollection.getAllVMs())
avm.expireAllLeases();
}
void disableUntil(String host, long until) {
disableRequests.offer(new HostDisablePair(host, until));
}
private void disableVMs() {
if (disableRequests.peek() == null)
return;
List disablePairs = new LinkedList<>();
disableRequests.drainTo(disablePairs);
for (HostDisablePair hostDisablePair: disablePairs) {
final Optional vmByName = vmCollection.getVmByName(hostDisablePair.host);
if (vmByName.isPresent())
vmByName.get().setDisabledUntil(hostDisablePair.until);
}
}
void enableVM(String host) {
final Optional vmByName = vmCollection.getVmByName(host);
if(vmByName.isPresent())
vmByName.get().enable();
else
logger.warn("Can't enable host " + host + ", no such host");
}
String getHostnameFromVMId(String vmId) {
return vmIdToHostnameMap.get(vmId);
}
void setActiveVmGroupAttributeName(String attributeName) {
this.activeVmGroupAttributeName = attributeName;
}
void setActiveVmGroups(List vmGroups) {
activeVmGroups.setActiveVmGroups(vmGroups);
}
private boolean isInActiveVmGroup(AssignableVirtualMachine avm) {
final String attrValue = avm.getAttrValue(activeVmGroupAttributeName);
return isInActiveVmGroup(attrValue);
}
private boolean isInActiveVmGroup(String attrValue) {
return activeVmGroups.isActiveVmGroup(attrValue, false);
}
private void expireAnyUnknownLeaseIds() {
if(!unknownLeaseIdsToExpire.isEmpty()) {
for(String leaseId: unknownLeaseIdsToExpire) {
final String hostname = leaseIdToHostnameMap.get(leaseId);
if(hostname!=null)
internalExpireLease(leaseId, hostname);
}
unknownLeaseIdsToExpire.clear();
}
}
List prepareAndGetOrderedVMs(List newLeases, AtomicInteger rejectedCount) {
disableVMs();
removeExpiredLeases();
rejectedCount.addAndGet(addLeases(newLeases));
expireAnyUnknownLeaseIds();
List vms = new ArrayList<>();
taskTracker.clearAssignedTasks();
vmRejectLimiter.reset();
resetTotalResources();
// ToDo make this parallel maybe?
for(AssignableVirtualMachine avm: vmCollection.getAllVMs()) {
avm.prepareForScheduling();
if(isInActiveVmGroup(avm) && avm.isAssignableNow()) {
// for now, only add it if it is available right now
if(logger.isDebugEnabled())
logger.debug("Host " + avm.getHostname() + " available for assignments");
vms.add(avm);
}
else if(logger.isDebugEnabled())
logger.debug("Host " + avm.getHostname() + " not available for assignments");
saveMaxResources(avm);
if (isInActiveVmGroup(avm) && !avm.isDisabled())
addTotalResources(avm);
}
taskTracker.setTotalResources(totalResourcesMap);
//Collections.sort(vms);
return vms;
}
List getInactiveVMs() {
return vmCollection.getAllVMs().stream().filter(avm -> !isInActiveVmGroup(avm)).collect(Collectors.toList());
}
private void resetTotalResources() {
totalResourcesMap.clear();
}
private void addTotalResources(AssignableVirtualMachine avm) {
final Map maxResources = avm.getMaxResources();
for (VMResource r: maxResources.keySet()) {
Double v = maxResources.get(r);
if (v != null) {
if (totalResourcesMap.get(r) == null)
totalResourcesMap.put(r, v);
else
totalResourcesMap.put(r, totalResourcesMap.get(r) + v);
}
}
}
private void removeExpiredLeases() {
for(AssignableVirtualMachine avm: vmCollection.getAllVMs())
avm.removeExpiredLeases(!isInActiveVmGroup(avm));
}
int removeLimitedLeases(List idleResourcesList) {
int rejected=0;
List randomized = new ArrayList<>(idleResourcesList);
// randomize the list so we don't always reject leases of the same VM before hitting the reject limit
Collections.shuffle(randomized);
for(VirtualMachineLease lease: randomized) {
if(vmRejectLimiter.limitReached())
break;
final Optional vmByName = vmCollection.getVmByName(lease.hostname());
if (vmByName.isPresent())
rejected += vmByName.get().expireLimitedLeases(vmRejectLimiter);
}
return rejected;
}
int getTotalNumVMs() {
return vmCollection.size();
}
void purgeInactiveVMs(Set excludeVms) {
for(AssignableVirtualMachine avm: vmCollection.getAllVMs()) {
if(avm != null) {
if (!excludeVms.contains(avm.getHostname())) {
if (!avm.isActive()) {
vmCollection.remove(avm);
if (avm.getCurrVMId() != null)
vmIdToHostnameMap.remove(avm.getCurrVMId(), avm.getHostname());
logger.info("Removed inactive host " + avm.getHostname());
}
}
}
}
}
private void saveMaxResources(AssignableVirtualMachine avm) {
if(attrNameToGroupMaxResources!=null && !attrNameToGroupMaxResources.isEmpty()) {
String attrValue = avm.getAttrValue(attrNameToGroupMaxResources);
if(attrValue !=null) {
Map maxResources = avm.getMaxResources();
Map savedMaxResources = maxResourcesMap.get(attrValue);
if(savedMaxResources==null) {
savedMaxResources = new HashMap<>();
maxResourcesMap.put(attrValue, savedMaxResources);
}
for(VMResource r: VMResource.values()) {
switch (r) {
case CPU:
case Disk:
case Memory:
case Ports:
case Network:
Double savedVal = savedMaxResources.get(r)==null? 0.0 : savedMaxResources.get(r);
savedMaxResources.put(r, Math.max(savedVal, maxResources.get(r)));
}
}
}
}
}
Map getMaxResources(String attrValue) {
return maxResourcesMap.get(attrValue);
}
AssignmentFailure getFailedMaxResource(String attrValue, TaskRequest task) {
AssignmentFailure savedFailure = null;
for(Map.Entry> entry: maxResourcesMap.entrySet()) {
if(attrValue!=null && !attrValue.equals(entry.getKey()))
continue;
final Map maxResources = entry.getValue();
AssignmentFailure failure = null;
for(VMResource res: VMResource.values()) {
switch (res) {
case CPU:
if(maxResources.get(VMResource.CPU) < task.getCPUs()) {
failure = new AssignmentFailure(
VMResource.CPU, task.getCPUs(), 0.0, maxResources.get(VMResource.CPU), "");
}
break;
case Memory:
if(maxResources.get(VMResource.Memory) < task.getMemory())
failure = new AssignmentFailure(
VMResource.Memory, task.getMemory(), 0.0, maxResources.get(VMResource.Memory), "");
break;
case Disk:
if(maxResources.get(VMResource.Disk) < task.getDisk())
failure = new AssignmentFailure(
VMResource.Disk, task.getDisk(), 0.0, maxResources.get(VMResource.Disk), "");
break;
case Ports:
if(maxResources.get(VMResource.Ports) < task.getPorts())
failure = new AssignmentFailure(
VMResource.Ports, task.getPorts(), 0.0, maxResources.get(VMResource.Ports), "");
break;
case Network:
if(maxResources.get(VMResource.Network) < task.getNetworkMbps())
failure = new AssignmentFailure(
VMResource.Network, task.getNetworkMbps(), 0.0, maxResources.get(VMResource.Network), "");
break;
case VirtualMachine:
case Fitness:
case ResAllocs:
case ResourceSet:
case Other:
break;
default:
logger.error("Unknown resource type: " + res);
}
if(failure!=null)
break;
}
if(failure == null)
return null;
savedFailure = failure;
}
return savedFailure;
}
ActiveVmGroups getActiveVmGroups() {
return activeVmGroups;
}
List getVmCurrentStates() {
List result = new ArrayList<>();
for(AssignableVirtualMachine avm: vmCollection.getAllVMs())
result.add(avm.getVmCurrentState());
return result;
}
AssignableVirtualMachine getDummyVM() {
return dummyVM;
}
}