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); } }); } }