/***************************************************************************
* Copyright (c) 2013 VMware, Inc. 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.vmware.vhadoop.vhm.strategy;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Set;
import java.util.logging.Logger;
import com.vmware.vhadoop.api.vhm.ClusterMap;
import com.vmware.vhadoop.api.vhm.ClusterMapReader;
import com.vmware.vhadoop.api.vhm.strategy.VMChooser;
import com.vmware.vhadoop.vhm.AbstractClusterMapReader;
public class BalancedVMChooser extends AbstractClusterMapReader implements VMChooser, ClusterMapReader {
private static final Logger _log = Logger.getLogger(BalancedVMChooser.class.getName());
private class HostInfo {
public HostInfo(String hostId) {
_hostId = hostId;
}
String _hostId;
Set<String> _candidates = new LinkedHashSet<String>();
int _on;
}
private Set<RankedVM> rankVMs(final Queue<HostInfo> orderedHosts, final boolean targetPowerState) {
Set<RankedVM> result = new HashSet<RankedVM>();
int rank = 0;
HostInfo current = null;
do {
current = orderedHosts.poll();
if (current != null) {
Iterator<String> itr = current._candidates.iterator();
if (itr.hasNext()) {
String vmId = itr.next();
current._on += targetPowerState ? 1 : -1;
result.add(new RankedVM(vmId, rank++));
itr.remove();
}
if (itr.hasNext()) {
orderedHosts.add(current);
}
}
} while (current != null);
return result;
}
private Map<String, HostInfo> organizeVMsByHost(final String clusterId, final Set<String> candidateVmIds, final boolean targetPowerState) {
Map<String, HostInfo> hostMap = new HashMap<String, HostInfo>();
if (candidateVmIds.isEmpty()) {
return null;
}
ClusterMap clusterMap = null;
try {
clusterMap = getAndReadLockClusterMap();
for (String vmId : candidateVmIds) {
String hostId = clusterMap.getHostIdForVm(vmId);
if (hostId != null) {
HostInfo hostInfo = hostMap.get(hostId);
if (hostInfo == null) {
hostInfo = new HostInfo(hostId);
hostMap.put(hostId, hostInfo);
}
hostInfo._candidates.add(vmId);
}
}
for (Entry<String, HostInfo> entry : hostMap.entrySet()) {
Set<String> allVmsOnHostInPowerState = clusterMap.listComputeVMsForClusterHostAndPowerState(clusterId, entry.getKey(), true);
if (allVmsOnHostInPowerState != null) {
entry.getValue()._on = allVmsOnHostInPowerState.size();
}
Set<String> candidateVMs = entry.getValue()._candidates;
_log.info("found "+candidateVMs.size()+" candidate VMs on host "+entry.getKey());
for (String id : candidateVMs) {
_log.info("candidate VM on "+entry.getKey()+": <%V"+id);
}
}
} finally {
unlockClusterMap(clusterMap);
}
return hostMap;
}
private Queue<HostInfo> orderHosts(Map<String, HostInfo> hostMap, final boolean targetPowerState) {
PriorityQueue<HostInfo> orderedHosts = new PriorityQueue<HostInfo>(hostMap.size(), new Comparator<HostInfo>() {
@Override
public int compare(final HostInfo a, final HostInfo b) {
return targetPowerState ? a._on - b._on : b._on - a._on;
}
});
orderedHosts.addAll(hostMap.values());
return orderedHosts;
}
private Set<RankedVM> rankVMsForTargetPowerState(String clusterId, Set<String> candidateVmIds, final boolean targetPowerState) {
if (candidateVmIds != null) {
Map<String, HostInfo> organizedHosts = organizeVMsByHost(clusterId, candidateVmIds, targetPowerState);
if (organizedHosts != null) {
Queue<HostInfo> orderedHosts = orderHosts(organizedHosts, targetPowerState);
Set<RankedVM> result = rankVMs(orderedHosts, targetPowerState);
_log.info("BalancedVMChooser done ranking VMs for "+(targetPowerState ? "enabling" : "disabling")+": "+result);
return result;
}
}
_log.info("BalancedVMChooser could not find any VMs to rank");
return new HashSet<RankedVM>();
}
@Override
public Set<RankedVM> rankVMsToEnable(String clusterId, Set<String> candidateVmIds) {
return rankVMsForTargetPowerState(clusterId, candidateVmIds, true);
}
@Override
public Set<RankedVM> rankVMsToDisable(String clusterId, Set<String> candidateVmIds) {
return rankVMsForTargetPowerState(clusterId, candidateVmIds, false);
}
@Override
public Set<String> chooseVMsToEnable(String clusterId, Set<String> candidateVmIds) {
return null;
}
@Override
public Set<String> chooseVMsToDisable(String clusterId, Set<String> candidateVmIds) {
return null;
}
}