package org.zstack.compute.allocator;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.transaction.annotation.Transactional;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.core.db.DeadlockAutoRestart;
import org.zstack.header.allocator.HostCapacityVO;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;
import javax.persistence.LockModeType;
import javax.persistence.TypedQuery;
import java.util.List;
/**
* Created by frank on 11/2/2015.
*/
@Configurable(preConstruction = true, autowire = Autowire.BY_TYPE)
public class HostCapacityUpdater {
private static final CLogger logger = Utils.getLogger(HostCapacityUpdater.class);
@Autowired
private DatabaseFacade dbf;
private String hostUuid;
private TypedQuery<HostCapacityVO> query;
private HostCapacityVO capacityVO;
private HostCapacityVO originalCopy;
public HostCapacityUpdater(String hostUuid) {
this.hostUuid = hostUuid;
}
public HostCapacityUpdater(TypedQuery<HostCapacityVO> query) {
this.query = query;
}
private void logDeletedHost() {
logger.warn(String.format("[Host Capacity] unable to update capacity for the host[uuid:%s]. It may have been deleted, cannot find it in database",
hostUuid));
}
private void logCapacityChange() {
if (logger.isTraceEnabled()) {
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
int index = 0;
String fileName = HostCapacityUpdater.class.getSimpleName() + ".java";
for (int i=0; i<stackTraceElements.length; i++) {
if (fileName.equals(stackTraceElements[i].getFileName())) {
index = i;
}
}
StackTraceElement caller = stackTraceElements[index+1];
logger.trace(String.format("[Host Capacity] %s:%s:%s changed the capacity of the host[uuid:%s] as:\n" +
"total cpu: %s --> %s\n" +
"available cpu: %s --> %s\n" +
"total memory: %s --> %s\n" +
"available memory: %s --> %s\n" +
"total physical memory: %s --> %s\n" +
"available physical memory: %s --> %s\n",
caller.getFileName(), caller.getMethodName(), caller.getLineNumber(), capacityVO.getUuid(),
originalCopy.getTotalCpu(), capacityVO.getTotalCpu(),
originalCopy.getAvailableCpu(), capacityVO.getAvailableCpu(),
originalCopy.getTotalMemory(), capacityVO.getTotalMemory(),
originalCopy.getAvailableMemory(), capacityVO.getAvailableMemory(),
originalCopy.getTotalPhysicalMemory(), capacityVO.getTotalPhysicalMemory(),
originalCopy.getAvailablePhysicalMemory(), capacityVO.getAvailablePhysicalMemory()));
}
}
private boolean lockCapacity() {
if (hostUuid != null) {
capacityVO = dbf.getEntityManager().find(HostCapacityVO.class, hostUuid, LockModeType.PESSIMISTIC_WRITE);
} else if (query != null) {
query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
List<HostCapacityVO> caps = query.getResultList();
capacityVO = caps.isEmpty() ? null : caps.get(0);
}
if (capacityVO != null) {
originalCopy = new HostCapacityVO();
originalCopy.setTotalCpu(capacityVO.getTotalCpu());
originalCopy.setAvailableCpu(capacityVO.getAvailableCpu());
originalCopy.setTotalMemory(capacityVO.getTotalMemory());
originalCopy.setAvailableMemory(capacityVO.getAvailableMemory());
originalCopy.setTotalPhysicalMemory(capacityVO.getTotalPhysicalMemory());
originalCopy.setAvailablePhysicalMemory(capacityVO.getAvailablePhysicalMemory());
}
return capacityVO != null;
}
private void merge() {
capacityVO = dbf.getEntityManager().merge(capacityVO);
logCapacityChange();
}
@Transactional
private boolean _run(HostCapacityUpdaterRunnable runnable) {
if (!lockCapacity()) {
logDeletedHost();
return false;
}
HostCapacityVO cap = runnable.call(capacityVO);
if (cap != null) {
capacityVO = cap;
merge();
return true;
}
return false;
}
@DeadlockAutoRestart
public boolean run(HostCapacityUpdaterRunnable runnable) {
return _run(runnable);
}
}