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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rx.functions.Action1;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
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;
}
synchronized boolean reject() {
if(rejectedCount==limit)
return false;
rejectedCount++;
lastRejectAt = System.currentTimeMillis();
return true;
}
private void reset() {
if(System.currentTimeMillis() > (lastRejectAt + rejectDelay))
rejectedCount=0;
}
}
private final ConcurrentMap virtualMachinesMap;
private final Action1 leaseRejectAction;
private final long leaseOfferExpirySecs;
private static final Logger logger = LoggerFactory.getLogger(AssignableVMs.class);
private final ConcurrentMap leaseIdToHostnameMap = new ConcurrentHashMap<>();
private final ConcurrentMap vmIdToHostnameMap = new ConcurrentHashMap<>();
private final TaskTracker taskTracker = new TaskTracker();
private final String attrNameToGroupMaxResources;
private final Map> maxResourcesMap;
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(Action1 leaseRejectAction, long leaseOfferExpirySecs, String attrNameToGroupMaxResources) {
virtualMachinesMap = new ConcurrentHashMap<>();
this.leaseRejectAction = leaseRejectAction;
this.leaseOfferExpirySecs = leaseOfferExpirySecs;
this.attrNameToGroupMaxResources = attrNameToGroupMaxResources;
maxResourcesMap = new HashMap<>();
vmRejectLimiter = new VMRejectLimiter(4, leaseOfferExpirySecs); // ToDo make this configurable?
activeVmGroups = new ActiveVmGroups();
}
Map> getResourceStatus() {
Map> result = new HashMap<>();
for(AssignableVirtualMachine avm: virtualMachinesMap.values())
result.put(avm.getHostname(), avm.getResourceStatus());
return result;
}
void setTaskAssigned(TaskRequest request, String host) {
createAvmIfAbsent(host);
AssignableVirtualMachine avm = virtualMachinesMap.get(host);
avm.setAssignedTask(request);
}
void unAssignTask(String taskId, String host) {
AssignableVirtualMachine avm = virtualMachinesMap.get(host);
if(avm != null) {
avm.markTaskForUnassigning(taskId);
}
else
logger.warn("No VM for host " + host + " to unassign task " + taskId);
}
int addLeases(List leases) {
int rejected=0;
for(VirtualMachineLease l: leases) {
String host = l.hostname();
createAvmIfAbsent(host);
if(!virtualMachinesMap.get(host).addLease(l))
rejected++;
}
return rejected;
}
private void createAvmIfAbsent(String hostname) {
if(virtualMachinesMap.get(hostname) == null)
virtualMachinesMap.putIfAbsent(hostname,
new AssignableVirtualMachine(vmIdToHostnameMap, leaseIdToHostnameMap, hostname,
leaseRejectAction, leaseOfferExpirySecs, taskTracker));
}
void expireLease(String leaseId) {
final String hostname = leaseIdToHostnameMap.get(leaseId);
if(hostname==null) {
unknownLeaseIdsToExpire.add(leaseId);
return;
}
internalExpireLease(hostname, leaseId);
}
private void internalExpireLease(String leaseId, String hostname) {
AssignableVirtualMachine avm = virtualMachinesMap.get(hostname);
if(avm != null)
avm.expireLease(leaseId);
}
void expireAllLeases(String hostname) {
final AssignableVirtualMachine avm = virtualMachinesMap.get(hostname);
if(avm!=null)
avm.expireAllLeases();
}
void expireAllLeases() {
for(AssignableVirtualMachine avm: virtualMachinesMap.values())
avm.expireAllLeases();
}
void disableUntil(String host, long until) {
createAvmIfAbsent(host);
AssignableVirtualMachine avm = virtualMachinesMap.get(host);
avm.setDisabledUntil(until);
}
void enableVM(String host) {
AssignableVirtualMachine avm = virtualMachinesMap.get(host);
if(avm != null)
avm.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 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() {
expireAnyUnknownLeaseIds();
List vms = new ArrayList<>();
taskTracker.clearAssignedTasks();
// ToDo make this parallel
vmRejectLimiter.reset();
for(Map.Entry entry: virtualMachinesMap.entrySet()) {
AssignableVirtualMachine avm = entry.getValue();
avm.prepareForScheduling();
if(isInActiveVmGroup(entry.getValue()) && entry.getValue().isAssignableNow()) {
// for now, only add it if it is available right now
vms.add(avm);
}
saveMaxResources(avm);
}
//Collections.sort(vms);
return vms;
}
int cleanup() {
int rejected=0;
List randomized = new ArrayList<>(virtualMachinesMap.values());
// randomize the list so we don't always reject leases of the same VM before hitting the reject limit
Collections.shuffle(randomized);
for(AssignableVirtualMachine avm: randomized) {
rejected += avm.removeExpiredLeases(vmRejectLimiter, !isInActiveVmGroup(avm));
}
return rejected;
}
int getTotalNumVMs() {
return virtualMachinesMap.size();
}
/* package */ void purgeInactiveVMs() {
for(String hostname: virtualMachinesMap.keySet()) {
AssignableVirtualMachine avm = virtualMachinesMap.get(hostname);
if(avm != null) {
if(!avm.isActive()) {
virtualMachinesMap.remove(hostname, avm);
if(avm.getCurrVMId() != null)
vmIdToHostnameMap.remove(avm.getCurrVMId(), avm.getHostname());
logger.info("Removed inactive host " + hostname);
}
}
}
}
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) {
for(Map.Entry> entry: maxResourcesMap.entrySet()) {
if(attrValue!=null && !attrValue.equals(entry.getKey()))
continue;
final Map maxResources = entry.getValue();
for(VMResource res: VMResource.values()) {
switch (res) {
case CPU:
if(maxResources.get(VMResource.CPU) < task.getCPUs()) {
return new AssignmentFailure(VMResource.CPU, task.getCPUs(), 0.0, maxResources.get(VMResource.CPU));
}
break;
case Memory:
if(maxResources.get(VMResource.Memory) < task.getMemory())
return new AssignmentFailure(VMResource.Memory, task.getMemory(), 0.0, maxResources.get(VMResource.Memory));
break;
case Disk:
if(maxResources.get(VMResource.Disk) < task.getDisk())
return new AssignmentFailure(VMResource.Disk, task.getDisk(), 0.0, maxResources.get(VMResource.Disk));
break;
case Ports:
if(maxResources.get(VMResource.Ports) < task.getPorts())
return new AssignmentFailure(VMResource.Ports, task.getPorts(), 0.0, maxResources.get(VMResource.Ports));
break;
case Network:
if(maxResources.get(VMResource.Network) < task.getNetworkMbps())
return new AssignmentFailure(VMResource.Network, task.getNetworkMbps(), 0.0, maxResources.get(VMResource.Network));
break;
case VirtualMachine:
case Fitness:
break;
default:
logger.error("Unknown resource type: " + res);
}
}
return null; // at least one set of maxResources satisfies the task
}
return null;
}
ActiveVmGroups getActiveVmGroups() {
return activeVmGroups;
}
List getVmCurrentStates() {
List result = new ArrayList<>();
for(AssignableVirtualMachine avm: virtualMachinesMap.values())
result.add(avm.getVmCurrentState());
return result;
}
AssignableVirtualMachine getDummyVM() {
return dummyVM;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy