package org.zstack.storage.primary.smp;
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.asyncbatch.AsyncBatchRunner;
import org.zstack.core.asyncbatch.LoopAsyncBatch;
import org.zstack.core.componentloader.PluginRegistry;
import org.zstack.core.db.SimpleQuery;
import org.zstack.core.db.SimpleQuery.Op;
import org.zstack.header.cluster.ClusterVO;
import org.zstack.header.cluster.ClusterVO_;
import org.zstack.header.core.Completion;
import org.zstack.header.core.NoErrorCompletion;
import org.zstack.header.core.ReturnValueCompletion;
import org.zstack.header.errorcode.ErrorCode;
import org.zstack.header.errorcode.OperationFailureException;
import org.zstack.header.exception.CloudRuntimeException;
import org.zstack.header.host.*;
import org.zstack.header.message.Message;
import org.zstack.header.storage.primary.*;
import org.zstack.header.storage.primary.VolumeSnapshotCapability.VolumeSnapshotArrangementType;
import org.zstack.header.storage.snapshot.VolumeSnapshotInventory;
import org.zstack.header.volume.VolumeFormat;
import org.zstack.header.volume.VolumeVO;
import org.zstack.header.volume.VolumeVO_;
import org.zstack.storage.primary.PrimaryStorageBase;
import org.zstack.storage.primary.PrimaryStorageCapacityRecalculator;
import org.zstack.storage.primary.PrimaryStorageCapacityUpdater;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;
import static org.zstack.core.Platform.operr;
import javax.persistence.TypedQuery;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Created by xing5 on 2016/3/26.
*/
@Configurable(preConstruction = true, autowire = Autowire.BY_TYPE)
public class SMPPrimaryStorageBase extends PrimaryStorageBase {
private static final CLogger logger = Utils.getLogger(SMPPrimaryStorageBase.class);
@Autowired
private PluginRegistry pluginRgty;
public SMPPrimaryStorageBase(PrimaryStorageVO self) {
super(self);
}
private HypervisorFactory getHypervisorFactoryByHypervisorType(String hvType) {
for (HypervisorFactory f : pluginRgty.getExtensionList(HypervisorFactory.class)) {
if (hvType.equals(f.getHypervisorType())) {
return f;
}
}
throw new CloudRuntimeException(String.format("cannot find HypervisorFactory[type = %s]", hvType));
}
protected HypervisorFactory getHypervisorFactoryByHostUuid(String huuid) {
SimpleQuery<HostVO> q = dbf.createQuery(HostVO.class);
q.select(HostVO_.hypervisorType);
q.add(HostVO_.uuid, Op.EQ, huuid);
String hvType = q.findValue();
return getHypervisorFactoryByHypervisorType(hvType);
}
private HypervisorFactory getHypervisorFactoryByClusterUuid(String cuuid) {
SimpleQuery<ClusterVO> q = dbf.createQuery(ClusterVO.class);
q.select(ClusterVO_.hypervisorType);
q.add(ClusterVO_.uuid, Op.EQ, cuuid);
String hvType = q.findValue();
return getHypervisorFactoryByHypervisorType(hvType);
}
@Override
public void attachHook(String clusterUuid, final Completion completion) {
HypervisorBackend bkd = getHypervisorFactoryByClusterUuid(clusterUuid).getHypervisorBackend(self);
bkd.attachHook(clusterUuid, completion);
}
@Override
protected void handle(final InstantiateVolumeOnPrimaryStorageMsg msg) {
if (msg.getDestHost() == null) {
String hostUuid = getAvailableHostUuidForOperation();
if (hostUuid == null) {
throw new OperationFailureException(operr("the shared mount point primary storage[uuid:%s, name:%s] cannot find any " +
"available host in attached clusters for instantiating the volume", self.getUuid(), self.getName()));
}
msg.setDestHost(HostInventory.valueOf(dbf.findByUuid(hostUuid, HostVO.class)));
}
HypervisorFactory f = getHypervisorFactoryByHostUuid(msg.getDestHost().getUuid());
HypervisorBackend bkd = f.getHypervisorBackend(self);
bkd.handle(msg, new ReturnValueCompletion<InstantiateVolumeOnPrimaryStorageReply>(msg) {
@Override
public void success(InstantiateVolumeOnPrimaryStorageReply reply) {
bus.reply(msg, reply);
}
@Override
public void fail(ErrorCode errorCode) {
InstantiateVolumeOnPrimaryStorageReply reply = new InstantiateVolumeOnPrimaryStorageReply();
reply.setError(errorCode);
bus.reply(msg, reply);
}
});
}
@Override
protected void handle(final DeleteVolumeOnPrimaryStorageMsg msg) {
HypervisorType type = VolumeFormat.getMasterHypervisorTypeByVolumeFormat(msg.getVolume().getFormat());
HypervisorFactory f = getHypervisorFactoryByHypervisorType(type.toString());
final HypervisorBackend bkd = f.getHypervisorBackend(self);
bkd.handle(msg, new ReturnValueCompletion<DeleteVolumeOnPrimaryStorageReply>(msg) {
@Override
public void success(DeleteVolumeOnPrimaryStorageReply reply) {
logger.debug( String.format("successfully delete volume[uuid:%s]", msg.getVolume().getUuid()));
bus.reply(msg, reply);
}
@Override
public void fail(ErrorCode errorCode) {
logger.debug( String.format("can't delete volume[uuid:%s] right now, add a GC job", msg.getVolume().getUuid()));
SMPDeleteVolumeGC gc = new SMPDeleteVolumeGC();
gc.NAME = String.format("gc-smp-%s-volume-%s", self.getUuid(), msg.getVolume());
gc.primaryStorageUuid = self.getUuid();
gc.hypervisorType = type.toString();
gc.volume = msg.getVolume();
gc.submit(SMPPrimaryStorageGlobalConfig.GC_INTERVAL.value(Long.class), TimeUnit.SECONDS);
DeleteVolumeOnPrimaryStorageReply reply = new DeleteVolumeOnPrimaryStorageReply();
bus.reply(msg, reply);
}
});
}
@Override
protected void handle(final CreateTemplateFromVolumeOnPrimaryStorageMsg msg) {
HypervisorType type = VolumeFormat.getMasterHypervisorTypeByVolumeFormat(msg.getVolumeInventory().getFormat());
HypervisorFactory f = getHypervisorFactoryByHypervisorType(type.toString());
HypervisorBackend bkd = f.getHypervisorBackend(self);
bkd.handle(msg, new ReturnValueCompletion<CreateTemplateFromVolumeOnPrimaryStorageReply>(msg) {
@Override
public void success(CreateTemplateFromVolumeOnPrimaryStorageReply reply) {
bus.reply(msg, reply);
}
@Override
public void fail(ErrorCode errorCode) {
CreateTemplateFromVolumeOnPrimaryStorageReply reply = new CreateTemplateFromVolumeOnPrimaryStorageReply();
reply.setError(errorCode);
bus.reply(msg, reply);
}
});
}
@Override
protected void handle(final DownloadDataVolumeToPrimaryStorageMsg msg) {
HypervisorType type = VolumeFormat.getMasterHypervisorTypeByVolumeFormat(msg.getImage().getFormat());
HypervisorFactory f = getHypervisorFactoryByHypervisorType(type.toString());
HypervisorBackend bkd = f.getHypervisorBackend(self);
bkd.handle(msg, new ReturnValueCompletion<DownloadDataVolumeToPrimaryStorageReply>(msg) {
@Override
public void success(DownloadDataVolumeToPrimaryStorageReply reply) {
bus.reply(msg, reply);
}
@Override
public void fail(ErrorCode errorCode) {
DownloadDataVolumeToPrimaryStorageReply reply = new DownloadDataVolumeToPrimaryStorageReply();
reply.setError(errorCode);
bus.reply(msg, reply);
}
});
}
@Override
protected void handle(final DeleteBitsOnPrimaryStorageMsg msg) {
HypervisorFactory f = getHypervisorFactoryByHypervisorType(msg.getHypervisorType());
HypervisorBackend bkd = f.getHypervisorBackend(self);
bkd.handle(msg, new ReturnValueCompletion<DeleteBitsOnPrimaryStorageReply>(msg) {
@Override
public void success(DeleteBitsOnPrimaryStorageReply reply) {
bus.reply(msg, reply);
}
@Override
public void fail(ErrorCode errorCode) {
DeleteBitsOnPrimaryStorageReply reply = new DeleteBitsOnPrimaryStorageReply();
reply.setError(errorCode);
bus.reply(msg, reply);
}
});
}
@Override
protected void handle(final DownloadIsoToPrimaryStorageMsg msg) {
HypervisorFactory f = getHypervisorFactoryByHostUuid(msg.getDestHostUuid());
HypervisorBackend bkd = f.getHypervisorBackend(self);
bkd.handle(msg, new ReturnValueCompletion<DownloadIsoToPrimaryStorageReply>(msg) {
@Override
public void success(DownloadIsoToPrimaryStorageReply reply) {
bus.reply(msg, reply);
}
@Override
public void fail(ErrorCode error) {
DownloadIsoToPrimaryStorageReply reply = new DownloadIsoToPrimaryStorageReply();
reply.setError(error);
bus.reply(msg, reply);
}
});
}
@Override
protected void handle(final DeleteIsoFromPrimaryStorageMsg msg) {
HypervisorType type = VolumeFormat.getMasterHypervisorTypeByVolumeFormat(msg.getIsoSpec().getInventory().getFormat());
HypervisorFactory f = getHypervisorFactoryByHypervisorType(type.toString());
HypervisorBackend bkd = f.getHypervisorBackend(self);
bkd.handle(msg, new ReturnValueCompletion<DeleteIsoFromPrimaryStorageReply>(msg) {
@Override
public void success(DeleteIsoFromPrimaryStorageReply reply) {
bus.reply(msg, reply);
}
@Override
public void fail(ErrorCode error) {
DeleteIsoFromPrimaryStorageReply reply = new DeleteIsoFromPrimaryStorageReply();
reply.setError(error);
bus.reply(msg, reply);
}
});
}
@Override
protected void handle(AskVolumeSnapshotCapabilityMsg msg) {
AskVolumeSnapshotCapabilityReply reply = new AskVolumeSnapshotCapabilityReply();
VolumeSnapshotCapability capability = new VolumeSnapshotCapability();
capability.setSupport(true);
capability.setArrangementType(VolumeSnapshotArrangementType.CHAIN);
reply.setCapability(capability);
bus.reply(msg, reply);
}
@Override
protected void handle(final SyncVolumeSizeOnPrimaryStorageMsg msg) {
SimpleQuery<VolumeVO> q = dbf.createQuery(VolumeVO.class);
q.select(VolumeVO_.format);
q.add(VolumeVO_.uuid, Op.EQ, msg.getVolumeUuid());
String format = q.findValue();
HypervisorType type = VolumeFormat.getMasterHypervisorTypeByVolumeFormat(format);
HypervisorFactory f = getHypervisorFactoryByHypervisorType(type.toString());
HypervisorBackend bkd = f.getHypervisorBackend(self);
bkd.handle(msg, new ReturnValueCompletion<SyncVolumeSizeOnPrimaryStorageReply>(msg) {
@Override
public void success(SyncVolumeSizeOnPrimaryStorageReply returnValue) {
bus.reply(msg, returnValue);
}
@Override
public void fail(ErrorCode errorCode) {
SyncVolumeSizeOnPrimaryStorageReply reply = new SyncVolumeSizeOnPrimaryStorageReply();
reply.setError(errorCode);
bus.reply(msg, reply);
}
});
}
@Override
protected void connectHook(ConnectParam param, final Completion completion) {
SimpleQuery<PrimaryStorageClusterRefVO> q = dbf.createQuery(PrimaryStorageClusterRefVO.class);
q.select(PrimaryStorageClusterRefVO_.clusterUuid);
q.add(PrimaryStorageClusterRefVO_.primaryStorageUuid, Op.EQ, self.getUuid());
final List<String> clusterUuids = q.listValue();
if (clusterUuids.isEmpty()) {
completion.success();
return;
}
new LoopAsyncBatch<String>(completion) {
boolean success;
@Override
protected Collection<String> collect() {
return clusterUuids;
}
@Override
protected AsyncBatchRunner forEach(String item) {
return new AsyncBatchRunner() {
@Override
public void run(NoErrorCompletion completion) {
HypervisorBackend bkd = getHypervisorFactoryByClusterUuid(item).getHypervisorBackend(self);
bkd.connectByClusterUuid(item, new Completion(completion) {
@Override
public void success() {
success = true;
completion.done();
}
@Override
public void fail(ErrorCode errorCode) {
errors.add(errorCode);
completion.done();
}
});
}
};
}
@Override
protected void done() {
if (success) {
completion.success();
} else {
completion.fail(errf.stringToOperationError(
String.format("failed to connect to all clusters%s", clusterUuids), errors
));
}
}
}.start();
}
@Override
protected void pingHook(Completion completion) {
completion.success();
}
@Override
protected void syncPhysicalCapacity(ReturnValueCompletion<PhysicalCapacityUsage> completion) {
completion.fail(operr("not supported operation"));
}
@Override
public void handleLocalMessage(Message msg) {
if (msg instanceof TakeSnapshotMsg) {
handle((TakeSnapshotMsg) msg);
} else if (msg instanceof BackupVolumeSnapshotFromPrimaryStorageToBackupStorageMsg) {
handle((BackupVolumeSnapshotFromPrimaryStorageToBackupStorageMsg) msg);
} else if (msg instanceof CreateVolumeFromVolumeSnapshotOnPrimaryStorageMsg) {
handle((CreateVolumeFromVolumeSnapshotOnPrimaryStorageMsg) msg);
} else if (msg instanceof SMPPrimaryStorageHypervisorSpecificMessage) {
handle((SMPPrimaryStorageHypervisorSpecificMessage) msg);
} else if (msg instanceof UploadBitsToBackupStorageMsg) {
handle((UploadBitsToBackupStorageMsg) msg);
} else if (msg instanceof CreateTemporaryVolumeFromSnapshotMsg) {
handle((CreateTemporaryVolumeFromSnapshotMsg) msg);
} else if (msg instanceof SMPRecalculatePrimaryStorageCapacityMsg) {
handle((SMPRecalculatePrimaryStorageCapacityMsg) msg);
} else {
super.handleLocalMessage(msg);
}
}
protected void handle(SMPRecalculatePrimaryStorageCapacityMsg msg) {
if (msg.isRelease()) {
doReleasePrimaryStorageCapacity();
} else {
RecalculatePrimaryStorageCapacityMsg rmsg = new RecalculatePrimaryStorageCapacityMsg();
rmsg.setPrimaryStorageUuid(self.getUuid());
bus.makeLocalServiceId(rmsg, PrimaryStorageConstant.SERVICE_ID);
bus.send(rmsg);
}
}
private void doReleasePrimaryStorageCapacity() {
PrimaryStorageCapacityUpdater updater = new PrimaryStorageCapacityUpdater(self.getUuid());
updater.run(new PrimaryStorageCapacityUpdaterRunnable() {
@Override
public PrimaryStorageCapacityVO call(PrimaryStorageCapacityVO cap) {
cap.setAvailableCapacity(0L);
cap.setTotalCapacity(0L);
cap.setTotalPhysicalCapacity(0L);
cap.setAvailablePhysicalCapacity(0L);
cap.setSystemUsedCapacity(0L);
return cap;
}
});
}
private void handle(final CreateTemporaryVolumeFromSnapshotMsg msg) {
HypervisorFactory f = getHypervisorFactoryByHypervisorType(msg.getHypervisorType());
HypervisorBackend bkd = f.getHypervisorBackend(self);
bkd.handle(msg, new ReturnValueCompletion<CreateTemporaryVolumeFromSnapshotReply>(msg) {
@Override
public void success(CreateTemporaryVolumeFromSnapshotReply returnValue) {
bus.reply(msg, returnValue);
}
@Override
public void fail(ErrorCode errorCode) {
CreateTemporaryVolumeFromSnapshotReply reply = new CreateTemporaryVolumeFromSnapshotReply();
reply.setError(errorCode);
bus.reply(msg, reply);
}
});
}
private void handle(final UploadBitsToBackupStorageMsg msg) {
HypervisorFactory f = getHypervisorFactoryByHypervisorType(msg.getHypervisorType());
HypervisorBackend bkd = f.getHypervisorBackend(self);
bkd.handle(msg, new ReturnValueCompletion<UploadBitsToBackupStorageReply>(msg) {
@Override
public void success(UploadBitsToBackupStorageReply reply) {
bus.reply(msg, reply);
}
@Override
public void fail(ErrorCode errorCode) {
UploadBitsToBackupStorageReply reply = new UploadBitsToBackupStorageReply();
reply.setError(errorCode);
bus.reply(msg, reply);
}
});
}
private void handle(SMPPrimaryStorageHypervisorSpecificMessage msg) {
HypervisorFactory f = getHypervisorFactoryByHypervisorType(msg.getHypervisorType());
HypervisorBackend bkd = f.getHypervisorBackend(self);
bkd.handleHypervisorSpecificMessage(msg);
}
protected void handle(final MergeVolumeSnapshotOnPrimaryStorageMsg msg) {
HypervisorBackend bkd = getHypervisorBackendByVolumeUuid(msg.getTo().getUuid());
bkd.handle(msg, new ReturnValueCompletion<MergeVolumeSnapshotOnPrimaryStorageReply>(msg) {
@Override
public void success(MergeVolumeSnapshotOnPrimaryStorageReply returnValue) {
bus.reply(msg, returnValue);
}
@Override
public void fail(ErrorCode errorCode) {
MergeVolumeSnapshotOnPrimaryStorageReply reply = new MergeVolumeSnapshotOnPrimaryStorageReply();
reply.setError(errorCode);
bus.reply(msg, reply);
}
});
}
private void handle(final CreateVolumeFromVolumeSnapshotOnPrimaryStorageMsg msg) {
HypervisorBackend bkd = getHypervisorBackendByVolumeUuid(msg.getSnapshot().getVolumeUuid());
bkd.handle(msg, new ReturnValueCompletion<CreateVolumeFromVolumeSnapshotOnPrimaryStorageReply>(msg) {
@Override
public void success(CreateVolumeFromVolumeSnapshotOnPrimaryStorageReply returnValue) {
bus.reply(msg, returnValue);
}
@Override
public void fail(ErrorCode errorCode) {
CreateVolumeFromVolumeSnapshotOnPrimaryStorageReply reply = new CreateVolumeFromVolumeSnapshotOnPrimaryStorageReply();
reply.setError(errorCode);
bus.reply(msg, reply);
}
});
}
private void handle(BackupVolumeSnapshotFromPrimaryStorageToBackupStorageMsg msg) {
HypervisorBackend bkd = getHypervisorBackendByVolumeUuid(msg.getSnapshot().getVolumeUuid());
bkd.handle(msg, new ReturnValueCompletion<BackupVolumeSnapshotFromPrimaryStorageToBackupStorageReply>(msg) {
@Override
public void success(BackupVolumeSnapshotFromPrimaryStorageToBackupStorageReply returnValue) {
bus.reply(msg, returnValue);
}
@Override
public void fail(ErrorCode errorCode) {
BackupVolumeSnapshotFromPrimaryStorageToBackupStorageReply reply = new BackupVolumeSnapshotFromPrimaryStorageToBackupStorageReply();
reply.setError(errorCode);
bus.reply(msg, reply);
}
});
}
protected void handle(final RevertVolumeFromSnapshotOnPrimaryStorageMsg msg) {
HypervisorBackend bkd = getHypervisorBackendByVolumeUuid(msg.getVolume().getUuid());
bkd.handle(msg, new ReturnValueCompletion<RevertVolumeFromSnapshotOnPrimaryStorageReply>(msg) {
@Override
public void success(RevertVolumeFromSnapshotOnPrimaryStorageReply returnValue) {
bus.reply(msg, returnValue);
}
@Override
public void fail(ErrorCode errorCode) {
RevertVolumeFromSnapshotOnPrimaryStorageReply reply = new RevertVolumeFromSnapshotOnPrimaryStorageReply();
reply.setError(errorCode);
bus.reply(msg, reply);
}
});
}
protected void handle(final ReInitRootVolumeFromTemplateOnPrimaryStorageMsg msg) {
HypervisorBackend bkd = getHypervisorBackendByVolumeUuid(msg.getVolume().getUuid());
bkd.handle(msg, new ReturnValueCompletion<ReInitRootVolumeFromTemplateOnPrimaryStorageReply>(msg) {
@Override
public void success(ReInitRootVolumeFromTemplateOnPrimaryStorageReply returnValue) {
bus.reply(msg, returnValue);
}
@Override
public void fail(ErrorCode errorCode) {
ReInitRootVolumeFromTemplateOnPrimaryStorageReply reply = new ReInitRootVolumeFromTemplateOnPrimaryStorageReply();
reply.setError(errorCode);
bus.reply(msg, reply);
}
});
}
@Override
protected void handle(final DeleteSnapshotOnPrimaryStorageMsg msg) {
HypervisorBackend bkd = getHypervisorBackendByVolumeUuid(msg.getSnapshot().getVolumeUuid());
bkd.handle(msg, new ReturnValueCompletion<DeleteSnapshotOnPrimaryStorageReply>(msg) {
@Override
public void success(DeleteSnapshotOnPrimaryStorageReply returnValue) {
bus.reply(msg, returnValue);
}
@Override
public void fail(ErrorCode errorCode) {
DeleteSnapshotOnPrimaryStorageReply reply = new DeleteSnapshotOnPrimaryStorageReply();
reply.setError(errorCode);
bus.reply(msg, reply);
}
});
}
@Transactional(readOnly = true)
private String getAvailableHostUuidForOperation() {
String sql = "select host.uuid from PrimaryStorageClusterRefVO ref, HostVO host where" +
" ref.clusterUuid = host.clusterUuid and ref.primaryStorageUuid = :psUuid and host.status = :hstatus" +
" and host.state = :hstate";
TypedQuery<String> q = dbf.getEntityManager().createQuery(sql, String.class);
q.setParameter("psUuid", self.getUuid());
q.setParameter("hstatus", HostStatus.Connected);
q.setParameter("hstate", HostState.Enabled);
List<String> hostUuids = q.getResultList();
if (hostUuids.isEmpty()) {
return null;
}
Collections.shuffle(hostUuids);
return hostUuids.get(0);
}
private HypervisorBackend getHypervisorBackendByVolumeUuid(String volUuid) {
SimpleQuery<VolumeVO> q = dbf.createQuery(VolumeVO.class);
q.select(VolumeVO_.format);
q.add(VolumeVO_.uuid, Op.EQ, volUuid);
String format = q.findValue();
if (format == null) {
throw new CloudRuntimeException(String.format("cannot find the volume[uuid:%s]", volUuid));
}
HypervisorType type = VolumeFormat.getMasterHypervisorTypeByVolumeFormat(format);
HypervisorFactory f = getHypervisorFactoryByHypervisorType(type.toString());
return f.getHypervisorBackend(self);
}
private void handle(final TakeSnapshotMsg msg) {
final VolumeSnapshotInventory sp = msg.getStruct().getCurrent();
HypervisorBackend bkd = getHypervisorBackendByVolumeUuid(sp.getVolumeUuid());
bkd.handle(msg, new ReturnValueCompletion<TakeSnapshotReply>(msg) {
@Override
public void success(TakeSnapshotReply returnValue) {
bus.reply(msg, returnValue);
}
@Override
public void fail(ErrorCode errorCode) {
TakeSnapshotReply reply = new TakeSnapshotReply();
reply.setError(errorCode);
bus.reply(msg, reply);
}
});
}
}