package org.zstack.storage.primary.local;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.core.db.SimpleQuery;
import org.zstack.core.db.SimpleQuery.Op;
import org.zstack.header.allocator.AbstractHostAllocatorFlow;
import org.zstack.header.exception.CloudRuntimeException;
import org.zstack.header.host.HostVO;
import org.zstack.header.storage.primary.PrimaryStorageOverProvisioningManager;
import org.zstack.header.storage.snapshot.VolumeSnapshotVO;
import org.zstack.header.storage.snapshot.VolumeSnapshotVO_;
import org.zstack.header.vm.VmInstanceConstant.VmOperation;
import org.zstack.header.volume.VolumeInventory;
import org.zstack.utils.CollectionUtils;
import org.zstack.utils.function.Function;
import java.util.ArrayList;
import java.util.List;
/**
* Created by frank on 10/24/2015.
*/
@Configurable(preConstruction = true, autowire = Autowire.BY_TYPE)
public class AllocatePrimaryStorageForVmMigrationFlow extends AbstractHostAllocatorFlow {
@Autowired
private DatabaseFacade dbf;
@Autowired
private PrimaryStorageOverProvisioningManager ratioMgr;
@Override
public void allocate() {
throwExceptionIfIAmTheFirstFlow();
if (!VmOperation.Migrate.toString().equals(spec.getVmOperation())) {
throw new CloudRuntimeException("AllocatePrimaryStorageForVmMigrationFlow is only used for migrating vm");
}
String psUuid = spec.getVmInstance().getRootVolume().getPrimaryStorageUuid();
List<String> huuids = CollectionUtils.transformToList(candidates, new Function<String, HostVO>() {
@Override
public String call(HostVO arg) {
return arg.getUuid();
}
});
long volumeSize = 0;
List<String> volUuids = new ArrayList<>();
for (VolumeInventory vol : spec.getVmInstance().getAllVolumes()) {
volumeSize += vol.getSize();
volUuids.add(vol.getUuid());
}
long snapshotSize = 0;
SimpleQuery<VolumeSnapshotVO> sq = dbf.createQuery(VolumeSnapshotVO.class);
sq.select(VolumeSnapshotVO_.size);
sq.add(VolumeSnapshotVO_.volumeUuid, Op.IN, volUuids);
List<Long> snapshotSizes = sq.listValue();
for (Long s : snapshotSizes) {
snapshotSize += s;
}
SimpleQuery<LocalStorageHostRefVO> q = dbf.createQuery(LocalStorageHostRefVO.class);
q.add(LocalStorageHostRefVO_.hostUuid, Op.IN, huuids);
q.add(LocalStorageHostRefVO_.primaryStorageUuid, Op.EQ, psUuid);
List<LocalStorageHostRefVO> refs = q.list();
final List<String> hostUuids = new ArrayList<>();
for (LocalStorageHostRefVO ref : refs) {
if (ref.getAvailableCapacity() > ratioMgr.calculateByRatio(psUuid, volumeSize) + snapshotSize) {
hostUuids.add(ref.getHostUuid());
}
}
candidates = CollectionUtils.transformToList(candidates, new Function<HostVO, HostVO>() {
@Override
public HostVO call(HostVO arg) {
return hostUuids.contains(arg.getUuid()) ? arg : null;
}
});
if (candidates.isEmpty()) {
fail(String.format("no hosts can provide %s bytes for all volumes of the vm[uuid:%s]", volumeSize, spec.getVmInstance().getUuid()));
} else {
next(candidates);
}
}
}