package org.zstack.storage.primary.local; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import org.zstack.core.cloudbus.CloudBusCallBack; import org.zstack.core.cloudbus.CloudBusListCallBack; import org.zstack.core.componentloader.PluginRegistry; import org.zstack.core.db.*; import org.zstack.core.db.SimpleQuery.Op; import org.zstack.core.thread.AsyncThread; import org.zstack.core.thread.ChainTask; import org.zstack.core.thread.SyncTaskChain; import org.zstack.core.workflow.FlowChainBuilder; import org.zstack.core.workflow.ShareFlow; import org.zstack.header.apimediator.ApiMessageInterceptionException; import org.zstack.header.cluster.ClusterInventory; import org.zstack.header.cluster.ClusterVO; import org.zstack.header.cluster.ClusterVO_; import org.zstack.header.core.AsyncLatch; import org.zstack.header.core.Completion; import org.zstack.header.core.NoErrorCompletion; import org.zstack.header.core.ReturnValueCompletion; import org.zstack.header.core.workflow.*; import org.zstack.header.errorcode.ErrorCode; import org.zstack.header.errorcode.OperationFailureException; import org.zstack.header.errorcode.SysErrors; import org.zstack.header.exception.CloudRuntimeException; import org.zstack.header.host.*; import org.zstack.header.image.ImageInventory; import org.zstack.header.image.ImageVO; import org.zstack.header.message.APIMessage; import org.zstack.header.message.Message; import org.zstack.header.message.MessageReply; 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.storage.snapshot.VolumeSnapshotVO; import org.zstack.header.storage.snapshot.VolumeSnapshotVO_; import org.zstack.header.vm.VmInstanceVO; import org.zstack.header.vm.VmInstanceVO_; import org.zstack.header.volume.*; import org.zstack.storage.primary.PrimaryStorageBase; import org.zstack.storage.primary.PrimaryStorageCapacityUpdater; import org.zstack.storage.primary.PrimaryStoragePhysicalCapacityManager; import org.zstack.storage.primary.local.APIGetLocalStorageHostDiskCapacityReply.HostDiskCapacity; import org.zstack.storage.primary.local.MigrateBitsStruct.ResourceInfo; import org.zstack.utils.CollectionUtils; import org.zstack.utils.DebugUtils; import org.zstack.utils.Utils; import org.zstack.utils.function.Function; import org.zstack.utils.gson.JSONObjectUtil; import org.zstack.utils.logging.CLogger; import static org.zstack.core.Platform.err; import static org.zstack.core.Platform.operr; import javax.persistence.LockModeType; import javax.persistence.Query; import javax.persistence.Tuple; import javax.persistence.TypedQuery; import java.util.*; import java.util.concurrent.Callable; import static org.zstack.core.progress.ProgressReportService.createSubTaskProgress; import static org.zstack.utils.CollectionDSL.list; /** * Created by frank on 6/30/2015. */ public class LocalStorageBase extends PrimaryStorageBase { private static final CLogger logger = Utils.getLogger(LocalStorageBase.class); @Autowired private PluginRegistry pluginRgty; @Autowired protected PrimaryStorageOverProvisioningManager ratioMgr; @Autowired protected PrimaryStoragePhysicalCapacityManager physicalCapacityMgr; @Autowired private LocalStorageImageCleaner imageCacheCleaner; static class FactoryCluster { LocalStorageHypervisorFactory factory; List<ClusterInventory> clusters; } public LocalStorageBase(PrimaryStorageVO self) { super(self); } @Override public void detachHook(String clusterUuid, Completion completion) { SimpleQuery<ClusterVO> q = dbf.createQuery(ClusterVO.class); q.select(ClusterVO_.hypervisorType); q.add(ClusterVO_.uuid, Op.EQ, clusterUuid); String hvType = q.findValue(); LocalStorageHypervisorFactory f = getHypervisorBackendFactory(hvType); final LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); bkd.detachHook(clusterUuid, new Completion(completion) { @Override public void success() { syncPhysicalCapacity(new ReturnValueCompletion<PhysicalCapacityUsage>(null) { @Override public void success(PhysicalCapacityUsage returnValue) { setCapacity(null, null, returnValue.totalPhysicalSize, returnValue.availablePhysicalSize); } @Override public void fail(ErrorCode errorCode) { logger.warn(String.format("failed to sync the physical capacity on the local primary storage[uuid:%s], %s", self.getUuid(), errorCode)); } }); completion.success(); } @Override public void fail(ErrorCode errorCode) { completion.fail(errorCode); } }); } @Override public void handleApiMessage(APIMessage msg) { if (msg instanceof APIGetLocalStorageHostDiskCapacityMsg) { handle((APIGetLocalStorageHostDiskCapacityMsg) msg); } else if (msg instanceof APILocalStorageMigrateVolumeMsg) { handle((APILocalStorageMigrateVolumeMsg) msg); } else if (msg instanceof APILocalStorageGetVolumeMigratableHostsMsg) { handle((APILocalStorageGetVolumeMigratableHostsMsg) msg); } else { super.handleApiMessage(msg); } } @Transactional(readOnly = true) private void handle(APILocalStorageGetVolumeMigratableHostsMsg msg) { // this API does the best it can to find migratable hosts. // it doesn't count the base image size because the image may have // been deleted, and ZStack has to consult the host for the image size APILocalStorageGetVolumeMigratableReply reply = new APILocalStorageGetVolumeMigratableReply(); new SQLBatch() { @Override protected void scripts() { //1.count the image size of volume long size = SQL.New("select vol.size" + " from VolumeVO vol" + " where vol.uuid = :uuid") .param("uuid", msg.getVolumeUuid()).find(); size = ratioMgr.calculateByRatio(self.getUuid(), size); Long snapshotSize = SQL.New("select sum(sp.size)" + " from VolumeSnapshotVO sp" + " where sp.volumeUuid = :volUuid") .param("volUuid", msg.getVolumeUuid()).find(); if (snapshotSize != null) { size += snapshotSize; } //2.select hosts that have enough capacity double physicalThreshold = physicalCapacityMgr.getRatio(self.getUuid()); List<String> hostUuids = SQL.New("select href.hostUuid" + " from LocalStorageHostRefVO href" + " where href.hostUuid !=" + " (" + " select rref.hostUuid" + " from LocalStorageResourceRefVO rref" + " where rref.resourceUuid = :volUuid" + " and rref.resourceType = :rtype" + " )" + " and (href.totalPhysicalCapacity * (1.0 - :thres)) <= href.availablePhysicalCapacity" + " and href.availablePhysicalCapacity != 0" + " and href.availableCapacity >= :size" + " and href.primaryStorageUuid = :psUuid" + " group by href.hostUuid") .param("volUuid", msg.getVolumeUuid()) .param("rtype", VolumeVO.class.getSimpleName()) .param("thres", physicalThreshold) .param("size", size) .param("psUuid", self.getUuid()).list(); if (hostUuids.isEmpty()) { reply.setInventories(new ArrayList<HostInventory>()); bus.reply(msg, reply); return; } LinkedList<HostVO> hosts = new LinkedList<>(SQL.New("select h from HostVO h " + " where h.uuid in (:uuids)" + " and h.status = :hstatus") .param("uuids", hostUuids) .param("hstatus", HostStatus.Connected).list()); //3.check if the network environment meets the requirement of vm running after migrate When migrate the rootVolume Boolean isRootVolume = (Q.New(VolumeVO.class).select(VolumeVO_.type) .eq(VolumeVO_.uuid,msg.getVolumeUuid()) .findValue() == VolumeType.Root); if(isRootVolume){ Tuple tuple = Q.New(VmInstanceVO.class) .select(VmInstanceVO_.clusterUuid, VmInstanceVO_.uuid) .eq(VmInstanceVO_.rootVolumeUuid, msg.getVolumeUuid()).findTuple(); String originClusterUuid = tuple.get(0,String.class); String originVmUuid = tuple.get(1,String.class); if(originClusterUuid == null){ throw new ApiMessageInterceptionException( err(SysErrors.INTERNAL,"The clusterUuid of vm cannot be null when migrate the vm")); } for(int i = 0; i < hosts.size(); i++){ String destClusterUuid = Q.New(HostVO.class).select(HostVO_.clusterUuid) .eq(HostVO_.uuid,hosts.get(i).getUuid()).findValue(); if(!originClusterUuid.equals(destClusterUuid)){ List<String> originL2NetworkList = sql("select l2NetworkUuid from L3NetworkVO" + " where uuid in(select l3NetworkUuid from VmNicVO where vmInstanceUuid = :vmUuid)") .param("vmUuid",originVmUuid).list(); List<String> l2NetworkList = sql("select l2NetworkUuid from L2NetworkClusterRefVO" + " where clusterUuid = :clusterUuid") .param("clusterUuid",destClusterUuid).list(); for(String l2:originL2NetworkList){ if(!l2NetworkList.contains(l2)){ //remove inappropriate host from list hosts.remove(i); return; } } } } } reply.setInventories(HostInventory.valueOf(hosts)); } }.execute(); bus.reply(msg, reply); } private void handle(final APILocalStorageMigrateVolumeMsg msg) { final APILocalStorageMigrateVolumeEvent evt = new APILocalStorageMigrateVolumeEvent(msg.getId()); if (self.getState() == PrimaryStorageState.Disabled) { evt.setError(operr("The primary storage[uuid:%s] is disabled cold migrate is not allowed", msg.getPrimaryStorageUuid())); bus.publish(evt); return; } MigrateVolumeOnLocalStorageMsg mmsg = new MigrateVolumeOnLocalStorageMsg(); mmsg.setPrimaryStorageUuid(msg.getPrimaryStorageUuid()); mmsg.setDestHostUuid(msg.getDestHostUuid()); mmsg.setVolumeUuid(msg.getVolumeUuid()); bus.makeTargetServiceIdByResourceUuid(mmsg, PrimaryStorageConstant.SERVICE_ID, self.getUuid()); MigrateVolumeOverlayMsg omsg = new MigrateVolumeOverlayMsg(); omsg.setMessage(mmsg); omsg.setVolumeUuid(msg.getVolumeUuid()); bus.makeTargetServiceIdByResourceUuid(omsg, VolumeConstant.SERVICE_ID, msg.getVolumeUuid()); bus.send(omsg, new CloudBusCallBack(msg) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { evt.setError(reply.getError()); bus.publish(evt); return; } MigrateVolumeOnLocalStorageReply mr = reply.castReply(); evt.setInventory(mr.getInventory()); bus.publish(evt); } }); } private void handle(final MigrateVolumeOnLocalStorageMsg msg) { thdf.chainSubmit(new ChainTask(msg) { @Override public String getSyncSignature() { return String.format("migrate-volume-%s", msg.getVolumeUuid()); } @Override public void run(SyncTaskChain chain) { migrateVolume(msg, new NoErrorCompletion(msg, chain) { @Override public void done() { chain.next(); } }); } @Override public String getName() { return getSyncSignature(); } }); } private void migrateVolume(MigrateVolumeOnLocalStorageMsg msg, NoErrorCompletion completion) { MigrateVolumeOnLocalStorageReply reply = new MigrateVolumeOnLocalStorageReply(); SimpleQuery<LocalStorageResourceRefVO> refq = dbf.createQuery(LocalStorageResourceRefVO.class); refq.add(LocalStorageResourceRefVO_.resourceUuid, Op.EQ, msg.getVolumeUuid()); refq.add(LocalStorageResourceRefVO_.resourceType, Op.EQ, VolumeVO.class.getSimpleName()); LocalStorageResourceRefVO ref = refq.find(); if (ref == null) { reply.setError(operr("volume[uuid:%s] is not on the local storage anymore," + "it may have been deleted", msg.getVolumeUuid())); bus.reply(msg, reply); completion.done(); return; } if (ref.getHostUuid().equals(msg.getDestHostUuid())) { logger.debug(String.format("the volume[uuid:%s] is already on the host[uuid:%s], no need to migrate", msg.getVolumeUuid(), msg.getDestHostUuid())); bus.reply(msg, reply); completion.done(); return; } FlowChain chain = FlowChainBuilder.newShareFlowChain(); chain.setName(String.format("migrate-volume-%s-local-storage-%s-to-host-%s", msg.getVolumeUuid(), msg.getPrimaryStorageUuid(), msg.getDestHostUuid())); chain.then(new ShareFlow() { LocalStorageResourceRefVO volumeRefVO; List<LocalStorageResourceRefVO> snapshotRefVOS; LocalStorageResourceRefInventory ref; long requiredSize; List<VolumeSnapshotVO> snapshots; VolumeVO volume; MigrateBitsStruct struct = new MigrateBitsStruct(); LocalStorageHypervisorBackend bkd; { SimpleQuery<LocalStorageResourceRefVO> q = dbf.createQuery(LocalStorageResourceRefVO.class); q.add(LocalStorageResourceRefVO_.resourceType, Op.EQ, VolumeVO.class.getSimpleName()); q.add(LocalStorageResourceRefVO_.resourceUuid, Op.EQ, msg.getVolumeUuid()); volumeRefVO = q.find(); ref = LocalStorageResourceRefInventory.valueOf(volumeRefVO); SimpleQuery<VolumeSnapshotVO> sq = dbf.createQuery(VolumeSnapshotVO.class); sq.add(VolumeSnapshotVO_.volumeUuid, Op.EQ, ref.getResourceUuid()); snapshots = sq.list(); volume = dbf.findByUuid(ref.getResourceUuid(), VolumeVO.class); requiredSize = ratioMgr.calculateByRatio(self.getUuid(), ref.getSize()); ResourceInfo info = new ResourceInfo(); info.setResourceRef(ref); info.setPath(volume.getInstallPath()); struct = new MigrateBitsStruct(); struct.getInfos().add(info); struct.setDestHostUuid(msg.getDestHostUuid()); struct.setSrcHostUuid(ref.getHostUuid()); struct.setVolume(VolumeInventory.valueOf(volume)); if (!snapshots.isEmpty()) { List<String> spUuids = CollectionUtils.transformToList(snapshots, new Function<String, VolumeSnapshotVO>() { @Override public String call(VolumeSnapshotVO arg) { return arg.getUuid(); } }); SimpleQuery<LocalStorageResourceRefVO> rq = dbf.createQuery(LocalStorageResourceRefVO.class); rq.add(LocalStorageResourceRefVO_.resourceType, Op.EQ, VolumeSnapshotVO.class.getSimpleName()); rq.add(LocalStorageResourceRefVO_.resourceUuid, Op.IN, spUuids); snapshotRefVOS = rq.list(); for (final VolumeSnapshotVO vo : snapshots) { info = new ResourceInfo(); info.setPath(vo.getPrimaryStorageInstallPath()); info.setResourceRef(CollectionUtils.find(snapshotRefVOS, new Function<LocalStorageResourceRefInventory, LocalStorageResourceRefVO>() { @Override public LocalStorageResourceRefInventory call(LocalStorageResourceRefVO arg) { return arg.getResourceUuid().equals(vo.getUuid()) ? LocalStorageResourceRefInventory.valueOf(arg) : null; } })); if (info.getResourceRef() == null) { throw new CloudRuntimeException( String.format("cannot find reference of snapshot[uuid:%s, name:%s] on the local storage[uuid:%s, name:%s]", vo.getUuid(), vo.getName(), self.getUuid(), self.getName())); } struct.getInfos().add(info); requiredSize += vo.getSize(); } } LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(msg.getDestHostUuid()); bkd = f.getHypervisorBackend(self); } @Override public void setup() { flow(new Flow() { String __name__ = "reserve-capacity-on-dest-host"; @Override public void run(FlowTrigger trigger, Map data) { reserveCapacityOnHost(msg.getDestHostUuid(), requiredSize, self.getUuid()); trigger.next(); } @Override public void rollback(FlowRollback trigger, Map data) { returnStorageCapacityToHost(msg.getDestHostUuid(), requiredSize); trigger.rollback(); } }); List<Flow> flows = bkd.createMigrateBitsVolumeFlow(struct); for (Flow fl : flows) { flow(fl); } flow(new NoRollbackFlow() { String __name__ = "change-reference-to-dst-host"; @Override public void run(FlowTrigger trigger, Map data) { List<String> resourceUuids = new ArrayList<>(); resourceUuids.add(volumeRefVO.getResourceUuid()); if (snapshotRefVOS != null) { for (LocalStorageResourceRefVO r : snapshotRefVOS) { resourceUuids.add(r.getResourceUuid()); } } UpdateQuery.New(LocalStorageResourceRefVO.class) .set(LocalStorageResourceRefVO_.hostUuid, msg.getDestHostUuid()) .condAnd(LocalStorageResourceRefVO_.resourceUuid, Op.IN, resourceUuids) .update(); trigger.next(); } }); flow(new NoRollbackFlow() { String __name__ = "return-capacity-to-src-host"; @Override public void run(FlowTrigger trigger, Map data) { returnStorageCapacityToHost(ref.getHostUuid(), requiredSize); trigger.next(); } }); flow(new NoRollbackFlow() { String __name__ = "delete-bits-on-the-src-host"; @Override public void run(FlowTrigger trigger, Map data) { List<String> paths = new ArrayList<>(); paths.add(volume.getInstallPath()); for (VolumeSnapshotVO sp : snapshots) { paths.add(sp.getPrimaryStorageInstallPath()); } final Iterator<String> it = paths.iterator(); new Runnable() { @Override @AsyncThread public void run() { if (!it.hasNext()) { return; } final String path = it.next(); bkd.deleteBits(path, struct.getSrcHostUuid(), new Completion(trigger) { @Override public void success() { run(); } @Override public void fail(ErrorCode errorCode) { //TODO GC logger.warn(String.format("failed to delete %s on the host[uuid:%s], %s", path, struct.getSrcHostUuid(), errorCode)); run(); } }); } }.run(); trigger.next(); } }); done(new FlowDoneHandler(msg, completion) { @Override public void handle(Map data) { new SQLBatch(){ //migrate the rooVolume and need to update the ClusterUuid of vm @Override protected void scripts() { Boolean isRootVolume = (Q.New(VolumeVO.class).select(VolumeVO_.type) .eq(VolumeVO_.uuid,volumeRefVO.getResourceUuid()) .findValue() == VolumeType.Root); if(isRootVolume){ Tuple tuple = Q.New(VmInstanceVO.class) .select(VmInstanceVO_.clusterUuid, VmInstanceVO_.uuid) .eq(VmInstanceVO_.rootVolumeUuid, volumeRefVO.getResourceUuid()).findTuple(); String originClusterUuid = tuple.get(0,String.class); String vmUuid = tuple.get(1,String.class); String clusterUuid = Q.New(HostVO.class).select(HostVO_.clusterUuid) .eq(HostVO_.uuid,msg.getDestHostUuid()).findValue(); if(!originClusterUuid.equals(clusterUuid)){ sql("update VmInstanceEO" + " set clusterUuid = :clusterUuid" + " where uuid = :vmUuid") .param("clusterUuid",clusterUuid) .param("vmUuid",vmUuid).execute(); } } LocalStorageResourceRefVO vo = Q.New(LocalStorageResourceRefVO.class) .eq(LocalStorageResourceRefVO_.resourceUuid, volumeRefVO.getResourceUuid()) .eq(LocalStorageResourceRefVO_.primaryStorageUuid, volumeRefVO.getPrimaryStorageUuid()) .eq(LocalStorageResourceRefVO_.hostUuid, msg.getDestHostUuid()) .find(); reply.setInventory(LocalStorageResourceRefInventory.valueOf(vo)); } }.execute(); bus.reply(msg, reply); } }); error(new FlowErrorHandler(msg, completion) { @Override public void handle(ErrorCode errCode, Map data) { reply.setError(errCode); bus.reply(msg, reply); } }); Finally(new FlowFinallyHandler(msg, completion) { @Override public void Finally() { completion.done(); } }); } }).start(); } @Override public void handleLocalMessage(Message msg) { if (msg instanceof InitPrimaryStorageOnHostConnectedMsg) { handle((InitPrimaryStorageOnHostConnectedMsg) msg); } else if (msg instanceof RemoveHostFromLocalStorageMsg) { handle((RemoveHostFromLocalStorageMsg) msg); } else 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 DownloadImageToPrimaryStorageCacheMsg) { handle((DownloadImageToPrimaryStorageCacheMsg) msg); } else if (msg instanceof LocalStorageCreateEmptyVolumeMsg) { handle((LocalStorageCreateEmptyVolumeMsg) msg); } else if (msg instanceof LocalStorageDirectlyDeleteBitsMsg) { handle((LocalStorageDirectlyDeleteBitsMsg) msg); } else if (msg instanceof LocalStorageReserveHostCapacityMsg) { handle((LocalStorageReserveHostCapacityMsg) msg); } else if (msg instanceof LocalStorageReturnHostCapacityMsg) { handle((LocalStorageReturnHostCapacityMsg) msg); } else if (msg instanceof LocalStorageHypervisorSpecificMessage) { handle((LocalStorageHypervisorSpecificMessage) msg); } else if (msg instanceof CreateTemporaryVolumeFromSnapshotMsg) { handle((CreateTemporaryVolumeFromSnapshotMsg) msg); } else if (msg instanceof UploadBitsFromLocalStorageToBackupStorageMsg) { handle((UploadBitsFromLocalStorageToBackupStorageMsg) msg); } else if (msg instanceof GetVolumeRootImageUuidFromPrimaryStorageMsg) { handle((GetVolumeRootImageUuidFromPrimaryStorageMsg) msg); } else if (msg instanceof LocalStorageDeleteImageCacheOnPrimaryStorageMsg) { handle((LocalStorageDeleteImageCacheOnPrimaryStorageMsg) msg); } else if (msg instanceof MigrateVolumeOnLocalStorageMsg) { handle((MigrateVolumeOnLocalStorageMsg) msg); } else { super.handleLocalMessage(msg); } } @Override protected void handle(APICleanUpImageCacheOnPrimaryStorageMsg msg) { APICleanUpImageCacheOnPrimaryStorageEvent evt = new APICleanUpImageCacheOnPrimaryStorageEvent(msg.getId()); imageCacheCleaner.cleanup(msg.getUuid()); bus.publish(evt); } private void handle(final LocalStorageDeleteImageCacheOnPrimaryStorageMsg msg) { LocalStorageHypervisorBackend bkd = getHypervisorBackendFactoryByHostUuid(msg.getHostUuid()).getHypervisorBackend(self); bkd.handle(msg, msg.getHostUuid(), new ReturnValueCompletion<DeleteImageCacheOnPrimaryStorageReply>(msg) { @Override public void success(DeleteImageCacheOnPrimaryStorageReply reply) { bus.reply(msg, reply); } @Override public void fail(ErrorCode errorCode) { DeleteImageCacheOnPrimaryStorageReply reply = new DeleteImageCacheOnPrimaryStorageReply(); reply.setError(errorCode); bus.reply(msg, reply); } }); } private void handle(final GetVolumeRootImageUuidFromPrimaryStorageMsg msg) { String hostUuid = getHostUuidByResourceUuid(msg.getVolume().getUuid()); LocalStorageHypervisorBackend bkd = getHypervisorBackendFactoryByHostUuid(hostUuid).getHypervisorBackend(self); bkd.handle(msg, hostUuid, new ReturnValueCompletion<GetVolumeRootImageUuidFromPrimaryStorageReply>(msg) { @Override public void success(GetVolumeRootImageUuidFromPrimaryStorageReply reply) { bus.reply(msg, reply); } @Override public void fail(ErrorCode errorCode) { GetVolumeRootImageUuidFromPrimaryStorageReply reply = new GetVolumeRootImageUuidFromPrimaryStorageReply(); reply.setError(errorCode); bus.reply(msg, reply); } }); } private void handle(final UploadBitsFromLocalStorageToBackupStorageMsg msg) { LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(msg.getHostUuid()); LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); bkd.handle(msg, msg.getHostUuid(), new ReturnValueCompletion<UploadBitsFromLocalStorageToBackupStorageReply>(msg) { @Override public void success(UploadBitsFromLocalStorageToBackupStorageReply returnValue) { bus.reply(msg, returnValue); } @Override public void fail(ErrorCode errorCode) { UploadBitsFromLocalStorageToBackupStorageReply reply = new UploadBitsFromLocalStorageToBackupStorageReply(); bus.reply(msg, reply); } }); } private void handle(final CreateTemporaryVolumeFromSnapshotMsg msg) { String hostUuid = getHostUuidByResourceUuid(msg.getSnapshot().getUuid()); LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(hostUuid); LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); bkd.handle(msg, hostUuid, 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(LocalStorageHypervisorSpecificMessage msg) { LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(msg.getHostUuid()); LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); bkd.handleHypervisorSpecificMessage(msg); } private void handle(LocalStorageReturnHostCapacityMsg msg) { LocalStorageReturnHostCapacityReply reply = new LocalStorageReturnHostCapacityReply(); long size = msg.isNoOverProvisioning() ? msg.getSize() : ratioMgr.calculateByRatio(self.getUuid(), msg.getSize()); returnStorageCapacityToHost(msg.getHostUuid(), size); bus.reply(msg, reply); } private void handle(LocalStorageReserveHostCapacityMsg msg) { LocalStorageReserveHostCapacityReply reply = new LocalStorageReserveHostCapacityReply(); long size = msg.isNoOverProvisioning() ? msg.getSize() : ratioMgr.calculateByRatio(self.getUuid(), msg.getSize()); reserveCapacityOnHost(msg.getHostUuid(), size, self.getUuid()); bus.reply(msg, reply); } private void handle(final LocalStorageDirectlyDeleteBitsMsg msg) { LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(msg.getHostUuid()); LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); bkd.handle(msg, msg.getHostUuid(), new ReturnValueCompletion<LocalStorageDirectlyDeleteBitsReply>(msg) { @Override public void success(LocalStorageDirectlyDeleteBitsReply reply) { bus.reply(msg, reply); } @Override public void fail(ErrorCode errorCode) { LocalStorageDirectlyDeleteBitsReply reply = new LocalStorageDirectlyDeleteBitsReply(); reply.setError(errorCode); bus.reply(msg, reply); } }); } private void handle(final LocalStorageCreateEmptyVolumeMsg msg) { LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(msg.getHostUuid()); LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); bkd.handle(msg, new ReturnValueCompletion<LocalStorageCreateEmptyVolumeReply>(msg) { @Override public void success(LocalStorageCreateEmptyVolumeReply returnValue) { bus.reply(msg, returnValue); } @Override public void fail(ErrorCode errorCode) { LocalStorageCreateEmptyVolumeReply reply = new LocalStorageCreateEmptyVolumeReply(); reply.setError(errorCode); bus.reply(msg, reply); } }); } private void handle(APIGetLocalStorageHostDiskCapacityMsg msg) { APIGetLocalStorageHostDiskCapacityReply reply = new APIGetLocalStorageHostDiskCapacityReply(); if (msg.getHostUuid() != null) { SimpleQuery<LocalStorageHostRefVO> q = dbf.createQuery(LocalStorageHostRefVO.class); q.add(LocalStorageHostRefVO_.primaryStorageUuid, Op.EQ, msg.getPrimaryStorageUuid()); q.add(LocalStorageHostRefVO_.hostUuid, Op.EQ, msg.getHostUuid()); LocalStorageHostRefVO ref = q.find(); if (ref == null) { reply.setError(errf.instantiateErrorCode(SysErrors.RESOURCE_NOT_FOUND, String.format("local primary storage[uuid:%s] doesn't have the host[uuid:%s]", self.getUuid(), msg.getHostUuid()))); bus.reply(msg, reply); return; } HostDiskCapacity c = new HostDiskCapacity(); c.setHostUuid(msg.getHostUuid()); c.setTotalCapacity(ref.getTotalCapacity()); c.setAvailableCapacity(ref.getAvailableCapacity()); c.setAvailablePhysicalCapacity(ref.getAvailablePhysicalCapacity()); c.setTotalPhysicalCapacity(ref.getTotalPhysicalCapacity()); reply.setInventories(list(c)); } else { SimpleQuery<LocalStorageHostRefVO> q = dbf.createQuery(LocalStorageHostRefVO.class); q.add(LocalStorageHostRefVO_.primaryStorageUuid, Op.EQ, msg.getPrimaryStorageUuid()); List<LocalStorageHostRefVO> refs = q.list(); List<HostDiskCapacity> cs = CollectionUtils.transformToList(refs, new Function<HostDiskCapacity, LocalStorageHostRefVO>() { @Override public HostDiskCapacity call(LocalStorageHostRefVO ref) { HostDiskCapacity c = new HostDiskCapacity(); c.setHostUuid(ref.getHostUuid()); c.setTotalCapacity(ref.getTotalCapacity()); c.setAvailableCapacity(ref.getAvailableCapacity()); c.setAvailablePhysicalCapacity(ref.getAvailablePhysicalCapacity()); c.setTotalPhysicalCapacity(ref.getTotalPhysicalCapacity()); return c; } }); reply.setInventories(cs); } bus.reply(msg, reply); } private void handle(final DownloadImageToPrimaryStorageCacheMsg msg) { ImageInventory imageInventory = msg.getImage(); // If image actualSize is null, Default allow distribute image long imageActualSize = imageInventory.getActualSize() != null ? imageInventory.getActualSize() : 0 ; final DownloadImageToPrimaryStorageCacheReply reply = new DownloadImageToPrimaryStorageCacheReply(); final List<String> hostUuids; if (msg.getHostUuid() == null) { hostUuids = new Callable<List<String>>() { @Override @Transactional(readOnly = true) public List<String> call() { String sql = "select h.hostUuid" + " from LocalStorageHostRefVO h, HostVO host" + " where h.primaryStorageUuid = :puuid" + " and h.hostUuid = host.uuid" + " and host.status = :hstatus" + " and h.availablePhysicalCapacity >= :availablePhysicalCapacity"; TypedQuery<String> q = dbf.getEntityManager().createQuery(sql, String.class); q.setParameter("puuid", self.getUuid()); q.setParameter("hstatus", HostStatus.Connected); q.setParameter("availablePhysicalCapacity", imageActualSize); return q.getResultList(); } }.call(); } else { hostUuids = list(msg.getHostUuid()); } if (hostUuids.isEmpty()) { bus.reply(msg, reply); return; } class HostError { ErrorCode errorCode; String hostUuid; } class Ret { List<HostError> errorCodes = new ArrayList<HostError>(); String installPath; synchronized void addError(HostError err) { errorCodes.add(err); } } final Ret ret = new Ret(); final AsyncLatch latch = new AsyncLatch(hostUuids.size(), new NoErrorCompletion(msg) { @Override public void done() { if (ret.errorCodes.size() == hostUuids.size()) { reply.setError(operr("failed to download image[uuid:%s] to all hosts in the local storage[uuid:%s]" + ". %s", msg.getImage().getUuid(), self.getUuid(), JSONObjectUtil.toJsonString(ret.errorCodes))); } else if (!ret.errorCodes.isEmpty()) { for (HostError err : ret.errorCodes) { logger.warn(String.format("failed to download image [uuid:%s] to the host[uuid:%s] in the local" + " storage[uuid:%s]. %s", msg.getImage().getUuid(), err.hostUuid, self.getUuid(), err.errorCode)); } } reply.setInstallPath(ret.installPath); bus.reply(msg, reply); } }); for (final String hostUuid : hostUuids) { FlowChain chain = FlowChainBuilder.newShareFlowChain(); chain.setName(String.format("download-image-%s-to-local-storage-%s-host-%s", msg.getImage().getUuid(), self.getUuid(), hostUuid)); chain.then(new ShareFlow() { @Override public void setup() { flow(new NoRollbackFlow() { String __name__ = "download-to-host"; @Override public void run(final FlowTrigger trigger, Map data) { LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(hostUuid); LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); bkd.downloadImageToCache(msg.getImage(), hostUuid, new ReturnValueCompletion<String>(trigger) { @Override public void success(String returnValue) { ret.installPath = returnValue; trigger.next(); } @Override public void fail(ErrorCode errorCode) { trigger.fail(errorCode); } }); } }); done(new FlowDoneHandler(latch) { @Override public void handle(Map data) { latch.ack(); } }); error(new FlowErrorHandler(latch) { @Override public void handle(ErrorCode errCode, Map data) { HostError herr = new HostError(); herr.errorCode = errCode; herr.hostUuid = hostUuid; ret.addError(herr); latch.ack(); } }); } }).start(); } } @Override protected void handle(final MergeVolumeSnapshotOnPrimaryStorageMsg msg) { final String hostUuid = getHostUuidByResourceUuid(msg.getTo().getUuid()); LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(hostUuid); LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); bkd.handle(msg, hostUuid, new ReturnValueCompletion<MergeVolumeSnapshotOnPrimaryStorageReply>(msg) { @Override public void success(MergeVolumeSnapshotOnPrimaryStorageReply returnValue) { bus.reply(msg, returnValue); } @Override public void fail(ErrorCode errorCode) { MergeVolumeSnapshotOnPrimaryStorageReply r = new MergeVolumeSnapshotOnPrimaryStorageReply(); r.setError(errorCode); bus.reply(msg, r); } }); } private void handle(final CreateVolumeFromVolumeSnapshotOnPrimaryStorageMsg msg) { final VolumeSnapshotInventory sinv = msg.getSnapshot(); final String hostUuid = getHostUuidByResourceUuid(sinv.getUuid()); if (hostUuid == null) { throw new OperationFailureException(errf.stringToInternalError( String.format("the volume snapshot[uuid:%s] is not on the local primary storage[uuid: %s]; the local primary storage" + " doesn't support the manner of downloading snapshots and creating the volume", sinv.getUuid(), self.getUuid()) )); } FlowChain chain = FlowChainBuilder.newShareFlowChain(); chain.setName(String.format("create-volume-%s-from-snapshots", msg.getVolumeUuid())); chain.then(new ShareFlow() { CreateVolumeFromVolumeSnapshotOnPrimaryStorageReply reply; @Override public void setup() { flow(new NoRollbackFlow() { String __name__ = "create-volume"; @Override public void run(final FlowTrigger trigger, Map data) { LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(hostUuid); LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); bkd.handle(msg, hostUuid, new ReturnValueCompletion<CreateVolumeFromVolumeSnapshotOnPrimaryStorageReply>(msg) { @Override public void success(CreateVolumeFromVolumeSnapshotOnPrimaryStorageReply returnValue) { reply = returnValue; trigger.next(); } @Override public void fail(ErrorCode errorCode) { trigger.fail(errorCode); } }); } }); flow(new Flow() { String __name__ = "reserve-capacity-on-host"; Long size; @Override public void run(FlowTrigger trigger, Map data) { size = reply.getSize(); reserveCapacityOnHost(hostUuid, size, self.getUuid()); trigger.next(); } @Override public void rollback(FlowRollback trigger, Map data) { if (size != null) { returnStorageCapacityToHost(hostUuid, size); } trigger.rollback(); } }); done(new FlowDoneHandler(msg) { @Override public void handle(Map data) { createResourceRefVO(msg.getVolumeUuid(), VolumeVO.class.getSimpleName(), reply.getSize(), hostUuid); bus.reply(msg, reply); } }); error(new FlowErrorHandler(msg) { @Override public void handle(ErrorCode errCode, Map data) { CreateVolumeFromVolumeSnapshotOnPrimaryStorageReply reply = new CreateVolumeFromVolumeSnapshotOnPrimaryStorageReply(); reply.setError(errCode); bus.reply(msg, reply); } }); } }).start(); } private void handle(final BackupVolumeSnapshotFromPrimaryStorageToBackupStorageMsg msg) { String hostUuid = getHostUuidByResourceUuid(msg.getSnapshot().getUuid()); LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(hostUuid); LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); bkd.handle(msg, hostUuid, 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) { final RevertVolumeFromSnapshotOnPrimaryStorageReply reply = new RevertVolumeFromSnapshotOnPrimaryStorageReply(); String hostUuid = getHostUuidByResourceUuid(msg.getSnapshot().getUuid()); LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(hostUuid); LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); bkd.handle(msg, hostUuid, new ReturnValueCompletion<RevertVolumeFromSnapshotOnPrimaryStorageReply>(msg) { @Override public void success(RevertVolumeFromSnapshotOnPrimaryStorageReply returnValue) { bus.reply(msg, returnValue); } @Override public void fail(ErrorCode errorCode) { reply.setError(errorCode); bus.reply(msg, reply); } }); } protected void handle(final ReInitRootVolumeFromTemplateOnPrimaryStorageMsg msg) { final ReInitRootVolumeFromTemplateOnPrimaryStorageReply reply = new ReInitRootVolumeFromTemplateOnPrimaryStorageReply(); String hostUuid = getHostUuidByResourceUuid(msg.getVolume().getUuid()); LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(hostUuid); LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); bkd.handle(msg, hostUuid, new ReturnValueCompletion<ReInitRootVolumeFromTemplateOnPrimaryStorageReply>(msg) { @Override public void success(ReInitRootVolumeFromTemplateOnPrimaryStorageReply returnValue) { bus.reply(msg, returnValue); } @Override public void fail(ErrorCode errorCode) { reply.setError(errorCode); bus.reply(msg, reply); } }); } protected String getHostUuidByResourceUuid(String resUuid) { SimpleQuery<LocalStorageResourceRefVO> q = dbf.createQuery(LocalStorageResourceRefVO.class); q.select(LocalStorageResourceRefVO_.hostUuid); q.add(LocalStorageResourceRefVO_.resourceUuid, Op.EQ, resUuid); return q.findValue(); } @Override protected void handle(final DeleteSnapshotOnPrimaryStorageMsg msg) { final String hostUuid = getHostUuidByResourceUuid(msg.getSnapshot().getUuid()); FlowChain chain = FlowChainBuilder.newShareFlowChain(); chain.setName(String.format("delete-snapshot-%s-on-local-storage-%s", msg.getSnapshot().getUuid(), self.getUuid())); chain.then(new ShareFlow() { DeleteSnapshotOnPrimaryStorageReply reply; @Override public void setup() { flow(new NoRollbackFlow() { String __name__ = "delete-snapshot-on-host"; @Override public void run(final FlowTrigger trigger, Map data) { LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(hostUuid); LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); bkd.handle(msg, hostUuid, new ReturnValueCompletion<DeleteSnapshotOnPrimaryStorageReply>(trigger) { @Override public void success(DeleteSnapshotOnPrimaryStorageReply returnValue) { reply = returnValue; trigger.next(); } @Override public void fail(ErrorCode errorCode) { trigger.fail(errorCode); } }); } }); flow(new NoRollbackFlow() { String __name__ = "return-capacity-to-host"; @Override public void run(FlowTrigger trigger, Map data) { returnStorageCapacityToHost(hostUuid, msg.getSnapshot().getSize()); trigger.next(); } }); done(new FlowDoneHandler(msg) { @Override public void handle(Map data) { deleteResourceRefVO(msg.getSnapshot().getUuid()); bus.reply(msg, reply); } }); error(new FlowErrorHandler(msg) { @Override public void handle(ErrorCode errCode, Map data) { DeleteSnapshotOnPrimaryStorageReply reply = new DeleteSnapshotOnPrimaryStorageReply(); reply.setError(errCode); bus.reply(msg, reply); } }); } }).start(); } private void handle(final TakeSnapshotMsg msg) { final VolumeSnapshotInventory sp = msg.getStruct().getCurrent(); final String hostUuid = getHostUuidByResourceUuid(sp.getVolumeUuid()); LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(hostUuid); LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); bkd.handle(msg, hostUuid, new ReturnValueCompletion<TakeSnapshotReply>(msg) { @Override public void success(TakeSnapshotReply returnValue) { createResourceRefVO(sp.getUuid(), VolumeSnapshotVO.class.getSimpleName(), returnValue.getInventory().getSize(), hostUuid); bus.reply(msg, returnValue); } @Override public void fail(ErrorCode errorCode) { TakeSnapshotReply reply = new TakeSnapshotReply(); reply.setError(errorCode); bus.reply(msg, reply); } }); } private void handle(RemoveHostFromLocalStorageMsg msg) { LocalStorageHostRefVO ref = Q.New(LocalStorageHostRefVO.class) .eq(LocalStorageHostRefVO_.hostUuid, msg.getHostUuid()) .eq(LocalStorageHostRefVO_.primaryStorageUuid, msg.getPrimaryStorageUuid()) .find(); if (ref != null) { // on remove, subtract the capacity from every capacity decreaseCapacity(ref.getTotalCapacity(), ref.getAvailableCapacity(), ref.getTotalPhysicalCapacity(), ref.getAvailablePhysicalCapacity(), ref.getSystemUsedCapacity()); dbf.remove(ref); } deleteResourceRef(msg.getHostUuid()); bus.reply(msg, new RemoveHostFromLocalStorageReply()); } void deleteResourceRef(String hostUuid) { new SQLBatch() { // dirty cleanup database for all possible related entities linking to the local storage. // basically, we cleanup, volumes, volume snapshots, image caches. // all below sql must be executed as the order they defined, DO NOT change anything unless you know // exactly what you are doing. // the MySQL won't support cascade trigger, which means when you delete a VM its nic will be // deleted accordingly but the trigger installed on the `AFTER DELETE ON VmNicVO` will not // be executed. // so we have to explicitly delete VmNicVO, VolumeVO then VmInstanceVO in order, to make // mysql triggers work in order to delete entities in AccountResourceRefVO, SystemVO etc. @Override protected void scripts() { List<LocalStorageResourceRefVO> refs = sql( "select ref from LocalStorageResourceRefVO ref where ref.hostUuid = :huuid" + " and ref.primaryStorageUuid = :psUuid", LocalStorageResourceRefVO.class ).param("huuid", hostUuid).param("psUuid", self.getUuid()).list(); if (refs.isEmpty()) { return; } List<String> volumesUuids = new ArrayList<>(); List<String> snapshotUuids = new ArrayList<>(); for (LocalStorageResourceRefVO ref : refs) { if (VolumeVO.class.getSimpleName().equals(ref.getResourceType())) { volumesUuids.add(ref.getResourceUuid()); } else if (VolumeSnapshotVO.class.getSimpleName().equals(ref.getResourceType())) { snapshotUuids.add(ref.getResourceUuid()); } } if (!snapshotUuids.isEmpty()) { sql("delete from VolumeSnapshotVO sp where sp.uuid in (:uuids)") .param("uuids", snapshotUuids).execute(); logger.debug(String.format("delete volume snapshots%s because the host[uuid:%s] is removed from" + " the local storage[name:%s, uuid:%s]", snapshotUuids, hostUuid, self.getName(), self.getUuid())); } if (!volumesUuids.isEmpty()) { List<String> vmUuidsToDelete = sql("select vm.uuid from VmInstanceVO vm where vm.rootVolumeUuid in" + " (select vol.uuid from VolumeVO vol where vol.uuid in (:volUuids)" + " and vol.type = :volType)", String.class) .param("volUuids", volumesUuids).param("volType", VolumeType.Root).list(); if (!vmUuidsToDelete.isEmpty()) { // delete vm nics sql("delete from VmNicVO nic where nic.vmInstanceUuid in (:uuids)") .param("uuids", vmUuidsToDelete).execute(); } // delete volumes including root and data volumes sql("delete from VolumeVO vol where vol.uuid in (:uuids)") .param("uuids", volumesUuids).execute(); logger.debug(String.format("delete volumes%s because the host[uuid:%s] is removed from" + " the local storage[name:%s, uuid:%s]", volumesUuids, hostUuid, self.getName(), self.getUuid())); if (!vmUuidsToDelete.isEmpty()) { // delete the vms sql("delete from VmInstanceVO vm where vm.uuid in (:uuids)") .param("uuids", vmUuidsToDelete).execute(); logger.debug(String.format("delete VMs%s because the host[uuid:%s] is removed from" + " the local storage[name:%s, uuid:%s]", vmUuidsToDelete, hostUuid, self.getName(), self.getUuid())); } } // delete the image cache sql("delete from ImageCacheVO ic where ic.primaryStorageUuid = :psUuid and" + " ic.installUrl like :url").param("psUuid", self.getUuid()) .param("url", String.format("%%%s%%", hostUuid)).execute(); for (LocalStorageResourceRefVO ref : refs) { dbf.getEntityManager().merge(ref); dbf.getEntityManager().remove(ref); } } }.execute(); } protected void handle(final InitPrimaryStorageOnHostConnectedMsg msg) { final InitPrimaryStorageOnHostConnectedReply reply = new InitPrimaryStorageOnHostConnectedReply(); LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(msg.getHostUuid()); final LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); bkd.handle(msg, new ReturnValueCompletion<PhysicalCapacityUsage>(msg) { @Override public void success(PhysicalCapacityUsage c) { List<LocalStorageHostRefVO> refs = Q.New(LocalStorageHostRefVO.class) .eq(LocalStorageHostRefVO_.hostUuid, msg.getHostUuid()) .eq(LocalStorageHostRefVO_.primaryStorageUuid, self.getUuid()) .list(); LocalStorageHostRefVO ref; if (refs == null || refs.isEmpty()) { ref = new LocalStorageHostRefVO(); ref.setTotalCapacity(c.totalPhysicalSize); ref.setAvailableCapacity(c.availablePhysicalSize); ref.setTotalPhysicalCapacity(c.totalPhysicalSize); ref.setAvailablePhysicalCapacity(c.availablePhysicalSize); ref.setHostUuid(msg.getHostUuid()); ref.setPrimaryStorageUuid(self.getUuid()); ref.setSystemUsedCapacity(c.totalPhysicalSize - c.availablePhysicalSize); dbf.persist(ref); increaseCapacity( c.totalPhysicalSize, c.availablePhysicalSize, c.totalPhysicalSize, c.availablePhysicalSize, ref.getSystemUsedCapacity()); } else { ref = refs.get(0); ref.setAvailablePhysicalCapacity(c.availablePhysicalSize); ref.setTotalPhysicalCapacity(c.totalPhysicalSize); ref.setTotalCapacity(c.totalPhysicalSize); dbf.update(ref); // the host's local storage capacity changed // need to recalculate the capacity in the database RecalculatePrimaryStorageCapacityMsg rmsg = new RecalculatePrimaryStorageCapacityMsg(); rmsg.setPrimaryStorageUuid(self.getUuid()); bus.makeTargetServiceIdByResourceUuid(rmsg, PrimaryStorageConstant.SERVICE_ID, self.getUuid()); bus.send(rmsg); } bus.reply(msg, reply); } @Override public void fail(ErrorCode errorCode) { reply.setError(errorCode); bus.reply(msg, reply); } }); } @Transactional protected void reserveCapacityOnHost(String hostUuid, long size, String psUuid) { String sql = "select ref" + " from LocalStorageHostRefVO ref" + " where ref.hostUuid = :huuid" + " and ref.primaryStorageUuid = :psUuid"; TypedQuery<LocalStorageHostRefVO> q = dbf.getEntityManager().createQuery(sql, LocalStorageHostRefVO.class); q.setParameter("huuid", hostUuid); q.setParameter("psUuid", psUuid); q.setLockMode(LockModeType.PESSIMISTIC_WRITE); List<LocalStorageHostRefVO> refs = q.getResultList(); if (refs.isEmpty()) { throw new CloudRuntimeException(String.format("cannot find host[uuid: %s] of local primary storage[uuid: %s]", hostUuid, self.getUuid())); } LocalStorageHostRefVO ref = refs.get(0); physicalCapacityMgr.checkCapacityByRatio( self.getUuid(), ref.getTotalPhysicalCapacity(), ref.getAvailablePhysicalCapacity() ); LocalStorageHostCapacityStruct s = new LocalStorageHostCapacityStruct(); s.setLocalStorage(getSelfInventory()); s.setHostUuid(ref.getHostUuid()); s.setSizeBeforeOverProvisioning(size); s.setSize(size); for (LocalStorageReserveHostCapacityExtensionPoint ext : pluginRgty.getExtensionList( LocalStorageReserveHostCapacityExtensionPoint.class)) { ext.beforeReserveLocalStorageCapacityOnHost(s); } long avail = ref.getAvailableCapacity() - s.getSize(); if (avail < 0) { throw new OperationFailureException(operr("host[uuid: %s] of local primary storage[uuid: %s] doesn't have enough capacity" + "[current: %s bytes, needed: %s]", hostUuid, self.getUuid(), ref.getAvailableCapacity(), size)); } ref.setAvailableCapacity(avail); dbf.getEntityManager().merge(ref); } @Transactional protected void returnStorageCapacityToHost(String hostUuid, long size) { String sql = "select ref from LocalStorageHostRefVO ref where ref.hostUuid = :huuid and ref.primaryStorageUuid = :primaryStorageUuid"; TypedQuery<LocalStorageHostRefVO> q = dbf.getEntityManager().createQuery(sql, LocalStorageHostRefVO.class); q.setParameter("huuid", hostUuid); q.setParameter("primaryStorageUuid", self.getUuid()); q.setLockMode(LockModeType.PESSIMISTIC_WRITE); List<LocalStorageHostRefVO> refs = q.getResultList(); if (refs.isEmpty()) { throw new CloudRuntimeException(String.format("cannot find host[uuid: %s] of local primary storage[uuid: %s]", hostUuid, self.getUuid())); } LocalStorageHostRefVO ref = refs.get(0); LocalStorageHostCapacityStruct s = new LocalStorageHostCapacityStruct(); s.setSizeBeforeOverProvisioning(size); s.setHostUuid(hostUuid); s.setLocalStorage(getSelfInventory()); s.setSize(size); for (LocalStorageReturnHostCapacityExtensionPoint ext : pluginRgty.getExtensionList( LocalStorageReturnHostCapacityExtensionPoint.class)) { ext.beforeReturnLocalStorageCapacityOnHost(s); } ref.setAvailableCapacity(ref.getAvailableCapacity() + s.getSize()); dbf.getEntityManager().merge(ref); } @Transactional protected void returnStorageCapacityToHostByResourceUuid(String resUuid) { String sql = "select href, rref" + " from LocalStorageHostRefVO href, LocalStorageResourceRefVO rref" + " where href.hostUuid = rref.hostUuid" + " and href.primaryStorageUuid = rref.primaryStorageUuid" + " and rref.resourceUuid = :resUuid" + " and rref.primaryStorageUuid = :puuid"; TypedQuery<Tuple> q = dbf.getEntityManager().createQuery(sql, Tuple.class); q.setLockMode(LockModeType.PESSIMISTIC_WRITE); q.setParameter("resUuid", resUuid); q.setParameter("puuid", self.getUuid()); List<Tuple> tupleList = q.getResultList(); if (tupleList == null || tupleList.isEmpty()) { return; } DebugUtils.Assert(tupleList.size() == 1, "should not get more than one LocalStorageHostRefVO/LocalStorageResourceRefVO"); Tuple twoRefs = tupleList.get(0); LocalStorageHostRefVO href = twoRefs.get(0, LocalStorageHostRefVO.class); LocalStorageResourceRefVO rref = twoRefs.get(1, LocalStorageResourceRefVO.class); long requiredSize = rref.getSize(); if (VolumeVO.class.getSimpleName().equals(rref.getResourceType())) { requiredSize = ratioMgr.calculateByRatio(self.getUuid(), requiredSize); } LocalStorageHostCapacityStruct s = new LocalStorageHostCapacityStruct(); s.setSizeBeforeOverProvisioning(rref.getSize()); s.setHostUuid(href.getHostUuid()); s.setLocalStorage(getSelfInventory()); s.setSize(requiredSize); for (LocalStorageReturnHostCapacityExtensionPoint ext : pluginRgty.getExtensionList( LocalStorageReturnHostCapacityExtensionPoint.class)) { ext.beforeReturnLocalStorageCapacityOnHost(s); } href.setAvailableCapacity(href.getAvailableCapacity() + s.getSize()); dbf.getEntityManager().merge(href); } @Override protected void handle(final InstantiateVolumeOnPrimaryStorageMsg msg) { createSubTaskProgress("create a volume[%s] on the local storage", msg.getVolume().getType()); String hostUuid = msg.getDestHost().getUuid(); LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(hostUuid); final LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); FlowChain chain = FlowChainBuilder.newShareFlowChain(); chain.setName(String.format("instantiate-volume-%s-local-primary-storage-%s", msg.getVolume().getUuid(), self.getUuid())); final String finalHostUuid = hostUuid; chain.then(new ShareFlow() { InstantiateVolumeOnPrimaryStorageReply reply; @Override public void setup() { flow(new Flow() { String __name__ = "allocate-capacity-on-host"; long requiredSize = ratioMgr.calculateByRatio(self.getUuid(), msg.getVolume().getSize()); long reservedSize; @Override public void run(FlowTrigger trigger, Map data) { reserveCapacityOnHost(finalHostUuid, requiredSize, self.getUuid()); reservedSize = requiredSize; trigger.next(); } @Override public void rollback(FlowRollback trigger, Map data) { if (reservedSize != 0) { returnStorageCapacityToHost(finalHostUuid, reservedSize); } trigger.rollback(); } }); flow(new NoRollbackFlow() { String __name__ = "instantiate-volume-on-host"; @Override public void run(final FlowTrigger trigger, Map data) { bkd.handle(msg, new ReturnValueCompletion<InstantiateVolumeOnPrimaryStorageReply>(msg) { @Override public void success(InstantiateVolumeOnPrimaryStorageReply returnValue) { reply = returnValue; trigger.next(); } @Override public void fail(ErrorCode errorCode) { trigger.fail(errorCode); } }); } }); done(new FlowDoneHandler(msg) { @Override public void handle(Map data) { createResourceRefVO(msg.getVolume().getUuid(), VolumeVO.class.getSimpleName(), msg.getVolume().getSize(), finalHostUuid); bus.reply(msg, reply); } }); error(new FlowErrorHandler(msg) { @Override public void handle(ErrorCode errCode, Map data) { InstantiateVolumeOnPrimaryStorageReply reply = new InstantiateVolumeOnPrimaryStorageReply(); reply.setError(errCode); bus.reply(msg, reply); } }); } }).start(); } private void deleteResourceRefVO(String resourceUuid) { SimpleQuery<LocalStorageResourceRefVO> q = dbf.createQuery(LocalStorageResourceRefVO.class); q.add(LocalStorageResourceRefVO_.primaryStorageUuid, Op.EQ, self.getUuid()); q.add(LocalStorageResourceRefVO_.resourceUuid, Op.EQ, resourceUuid); LocalStorageResourceRefVO ref = q.find(); dbf.remove(ref); } private void createResourceRefVO(String resUuid, String resType, long size, String hostUuid) { LocalStorageResourceRefVO ref = new LocalStorageResourceRefVO(); ref.setPrimaryStorageUuid(self.getUuid()); ref.setSize(size); ref.setResourceType(resType); ref.setResourceUuid(resUuid); ref.setHostUuid(hostUuid); dbf.persist(ref); } @Override protected void handle(final DeleteVolumeOnPrimaryStorageMsg msg) { SimpleQuery<LocalStorageResourceRefVO> q = dbf.createQuery(LocalStorageResourceRefVO.class); q.add(LocalStorageResourceRefVO_.resourceUuid, Op.EQ, msg.getVolume().getUuid()); q.add(LocalStorageResourceRefVO_.resourceType, Op.EQ, VolumeVO.class.getSimpleName()); if (!q.isExists()) { logger.debug(String.format("volume[uuid:%s] is not on the local storage[uuid:%s, name:%s]," + "the host the volume is on may have been deleted", msg.getVolume().getUuid(), self.getUuid(), self.getName())); DeleteVolumeOnPrimaryStorageReply reply = new DeleteVolumeOnPrimaryStorageReply(); bus.reply(msg, reply); return; } FlowChain chain = FlowChainBuilder.newShareFlowChain(); chain.setName(String.format("delete-volume-%s-local-primary-storage-%s", msg.getVolume().getUuid(), self.getUuid())); chain.then(new ShareFlow() { DeleteVolumeOnPrimaryStorageReply reply; @Override public void setup() { flow(new NoRollbackFlow() { String __name__ = "delete-volume-on-host"; @Override public void run(final FlowTrigger trigger, Map data) { LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByResourceUuid(msg.getVolume().getUuid(), VolumeVO.class.getSimpleName()); LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); bkd.handle(msg, new ReturnValueCompletion<DeleteVolumeOnPrimaryStorageReply>(msg) { @Override public void success(DeleteVolumeOnPrimaryStorageReply returnValue) { reply = returnValue; trigger.next(); } @Override public void fail(ErrorCode errorCode) { trigger.fail(errorCode); } }); } }); flow(new NoRollbackFlow() { String __name__ = "return-capacity-to-host"; @Override public void run(FlowTrigger trigger, Map data) { returnStorageCapacityToHostByResourceUuid(msg.getVolume().getUuid()); trigger.next(); } }); done(new FlowDoneHandler(msg) { @Override public void handle(Map data) { deleteResourceRefVO(msg.getVolume().getUuid()); bus.reply(msg, reply); } }); error(new FlowErrorHandler(msg) { @Override public void handle(ErrorCode errCode, Map data) { DeleteVolumeOnPrimaryStorageReply reply = new DeleteVolumeOnPrimaryStorageReply(); reply.setError(errCode); bus.reply(msg, reply); } }); } }).start(); } @Override protected void handle(CreateTemplateFromVolumeOnPrimaryStorageMsg msg) { LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByResourceUuid(msg.getVolumeInventory().getUuid(), VolumeVO.class.getSimpleName()); LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); bkd.handle(msg); } @Override protected void handle(final DownloadDataVolumeToPrimaryStorageMsg msg) { if (msg.getHostUuid() == null) { throw new OperationFailureException(operr("unable to create the data volume[uuid: %s] on a local primary storage[uuid:%s], because the hostUuid is not specified.", msg.getVolumeUuid(), self.getUuid())); } FlowChain chain = FlowChainBuilder.newShareFlowChain(); chain.setName(String.format("download-data-volume-%s-to-local-storage-%s", msg.getVolumeUuid(), self.getUuid())); chain.then(new ShareFlow() { DownloadDataVolumeToPrimaryStorageReply reply; long requiredSize = ratioMgr.calculateByRatio(self.getUuid(), msg.getImage().getSize()); @Override public void setup() { flow(new Flow() { String __name__ = "allocate-capacity-on-host"; @Override public void run(FlowTrigger trigger, Map data) { reserveCapacityOnHost(msg.getHostUuid(), requiredSize, self.getUuid()); trigger.next(); } @Override public void rollback(FlowRollback trigger, Map data) { returnStorageCapacityToHost(msg.getHostUuid(), requiredSize); trigger.rollback(); } }); flow(new NoRollbackFlow() { String __name__ = "download-the-data-volume-to-host"; @Override public void run(final FlowTrigger trigger, Map data) { LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(msg.getHostUuid()); LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); bkd.handle(msg, new ReturnValueCompletion<DownloadDataVolumeToPrimaryStorageReply>(trigger) { @Override public void success(DownloadDataVolumeToPrimaryStorageReply returnValue) { reply = returnValue; trigger.next(); } @Override public void fail(ErrorCode errorCode) { trigger.fail(errorCode); } }); } }); done(new FlowDoneHandler(msg) { @Override public void handle(Map data) { createResourceRefVO(msg.getVolumeUuid(), VolumeVO.class.getSimpleName(), msg.getImage().getSize(), msg.getHostUuid()); bus.reply(msg, reply); } }); error(new FlowErrorHandler(msg) { @Override public void handle(ErrorCode errCode, Map data) { DownloadDataVolumeToPrimaryStorageReply reply = new DownloadDataVolumeToPrimaryStorageReply(); reply.setError(errCode); bus.reply(msg, reply); } }); } }).start(); } @Override protected void handle(final DeleteBitsOnPrimaryStorageMsg msg) { FlowChain chain = FlowChainBuilder.newShareFlowChain(); chain.setName(String.format("delete-bits-on-local-primary-storage-%s", self.getUuid())); chain.then(new ShareFlow() { DeleteBitsOnPrimaryStorageReply reply; @Override public void setup() { flow(new NoRollbackFlow() { String __name__ = "delete-bits-on-host"; @Override public void run(final FlowTrigger trigger, Map data) { LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByResourceUuid(msg.getBitsUuid(), msg.getBitsType()); LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); bkd.handle(msg, new ReturnValueCompletion<DeleteBitsOnPrimaryStorageReply>(msg) { @Override public void success(DeleteBitsOnPrimaryStorageReply returnValue) { reply = returnValue; trigger.next(); } @Override public void fail(ErrorCode errorCode) { trigger.fail(errorCode); } }); } }); flow(new NoRollbackFlow() { String __name__ = "return-capacity-to-host"; @Override public void run(FlowTrigger trigger, Map data) { returnStorageCapacityToHostByResourceUuid(msg.getBitsUuid()); trigger.next(); } }); done(new FlowDoneHandler(msg) { @Override public void handle(Map data) { deleteResourceRefVO(msg.getBitsUuid()); bus.reply(msg, reply); } }); error(new FlowErrorHandler(msg) { @Override public void handle(ErrorCode errCode, Map data) { DeleteBitsOnPrimaryStorageReply reply = new DeleteBitsOnPrimaryStorageReply(); reply.setError(errCode); bus.reply(msg, reply); } }); } }).start(); } @Override protected void handle(final DownloadIsoToPrimaryStorageMsg msg) { FlowChain chain = FlowChainBuilder.newShareFlowChain(); chain.setName(String.format("download-iso-%s-local-primary-storage-%s", msg.getIsoSpec().getInventory().getUuid(), self.getUuid())); chain.then(new ShareFlow() { DownloadIsoToPrimaryStorageReply reply; @Override public void setup() { flow(new NoRollbackFlow() { String __name__ = "download-iso-to-host"; @Override public void run(final FlowTrigger trigger, Map data) { LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByHostUuid(msg.getDestHostUuid()); LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); bkd.handle(msg, new ReturnValueCompletion<DownloadIsoToPrimaryStorageReply>(msg) { @Override public void success(DownloadIsoToPrimaryStorageReply returnValue) { reply = returnValue; trigger.next(); } @Override public void fail(ErrorCode errorCode) { trigger.fail(errorCode); } }); } }); done(new FlowDoneHandler(msg) { @Override public void handle(Map data) { boolean isExists = Q.New(LocalStorageResourceRefVO.class) .eq(LocalStorageResourceRefVO_.resourceUuid, msg.getIsoSpec().getInventory().getUuid()) .eq(LocalStorageResourceRefVO_.resourceType, ImageVO.class.getSimpleName()) .eq(LocalStorageResourceRefVO_.primaryStorageUuid, self.getUuid()) .eq(LocalStorageResourceRefVO_.hostUuid, msg.getDestHostUuid()) .isExists(); if (!isExists) { createResourceRefVO( msg.getIsoSpec().getInventory().getUuid(), ImageVO.class.getSimpleName(), msg.getIsoSpec().getInventory().getActualSize(), msg.getDestHostUuid() ); } bus.reply(msg, reply); } }); error(new FlowErrorHandler(msg) { @Override public void handle(ErrorCode errCode, Map data) { DownloadIsoToPrimaryStorageReply reply = new DownloadIsoToPrimaryStorageReply(); reply.setError(errCode); bus.reply(msg, reply); } }); } }).start(); } @Override protected void handle(final DeleteIsoFromPrimaryStorageMsg msg) { FlowChain chain = FlowChainBuilder.newShareFlowChain(); chain.setName(String.format("delete-iso-local-primary-storage-%s", msg.getIsoSpec().getInventory().getUuid())); chain.then(new ShareFlow() { DeleteIsoFromPrimaryStorageReply reply; @Override public void setup() { flow(new NoRollbackFlow() { String __name__ = "delete-iso-on-host"; @Override public void run(final FlowTrigger trigger, Map data) { LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByResourceUuid( msg.getIsoSpec().getInventory().getUuid(), ImageVO.class.getSimpleName()); LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); bkd.handle(msg, new ReturnValueCompletion<DeleteIsoFromPrimaryStorageReply>(msg) { @Override public void success(DeleteIsoFromPrimaryStorageReply returnValue) { reply = returnValue; trigger.next(); } @Override public void fail(ErrorCode errorCode) { trigger.fail(errorCode); } }); } }); flow(new NoRollbackFlow() { String __name__ = "return-capacity-to-host"; @Override public void run(FlowTrigger trigger, Map data) { returnStorageCapacityToHostByResourceUuid(msg.getIsoSpec().getInventory().getUuid()); trigger.next(); } }); done(new FlowDoneHandler(msg) { @Override public void handle(Map data) { deleteResourceRefVO(msg.getIsoSpec().getInventory().getUuid()); bus.reply(msg, reply); } }); error(new FlowErrorHandler(msg) { @Override public void handle(ErrorCode errCode, Map data) { DeleteIsoFromPrimaryStorageReply reply = new DeleteIsoFromPrimaryStorageReply(); reply.setError(errCode); bus.reply(msg, reply); } }); } }).start(); } @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) { LocalStorageHypervisorFactory f = getHypervisorBackendFactoryByResourceUuid(msg.getVolumeUuid(), VolumeVO.class.getSimpleName()); LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); String huuid = getHostUuidByResourceUuid(msg.getVolumeUuid()); bkd.handle(msg, huuid, 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); } }); } protected void setCapacity(Long total, Long avail, Long totalPhysical, Long availPhysical) { PrimaryStorageCapacityUpdater updater = new PrimaryStorageCapacityUpdater(self.getUuid()); updater.update(total, avail, totalPhysical, availPhysical); } void increaseCapacity(final Long total, final Long avail, final Long totalPhysical, final Long availPhysical, final Long systemUsed) { PrimaryStorageCapacityUpdater updater = new PrimaryStorageCapacityUpdater(self.getUuid()); updater.run(new PrimaryStorageCapacityUpdaterRunnable() { @Override public PrimaryStorageCapacityVO call(PrimaryStorageCapacityVO cap) { if (total != null) { cap.setTotalCapacity(cap.getTotalCapacity() + total); } if (avail != null) { cap.setAvailableCapacity(cap.getAvailableCapacity() + avail); } if (totalPhysical != null) { cap.setTotalPhysicalCapacity(cap.getTotalPhysicalCapacity() + totalPhysical); } if (availPhysical != null) { cap.setAvailablePhysicalCapacity(cap.getAvailablePhysicalCapacity() + availPhysical); } if (systemUsed != null) { if (cap.getSystemUsedCapacity() == null) { cap.setSystemUsedCapacity(0L); } cap.setSystemUsedCapacity(cap.getSystemUsedCapacity() + systemUsed); } return cap; } }); } void decreaseCapacity(final Long total, final Long avail, final Long totalPhysical, final Long availPhysical, final Long systemUsed) { PrimaryStorageCapacityUpdater updater = new PrimaryStorageCapacityUpdater(self.getUuid()); updater.run(new PrimaryStorageCapacityUpdaterRunnable() { @Override public PrimaryStorageCapacityVO call(PrimaryStorageCapacityVO cap) { if (total != null) { long t = cap.getTotalCapacity() - total; cap.setTotalCapacity(t < 0 ? 0 : t); } if (avail != null) { // for over-provisioning scenarios, minus value of available capacity is permitted long a = cap.getAvailableCapacity() - avail; cap.setAvailableCapacity(a); } if (totalPhysical != null) { long tp = cap.getTotalPhysicalCapacity() - totalPhysical; cap.setTotalPhysicalCapacity(tp < 0 ? 0 : tp); } if (availPhysical != null) { long ap = cap.getAvailablePhysicalCapacity() - availPhysical; cap.setAvailablePhysicalCapacity(ap < 0 ? 0 : ap); } if (systemUsed != null) { long su = cap.getSystemUsedCapacity() - systemUsed; cap.setSystemUsedCapacity(su < 0 ? 0 : su); } return cap; } }); } @Transactional(readOnly = true) protected List<FactoryCluster> getAllFactoriesForAttachedClusters() { String sql = "select cluster" + " from ClusterVO cluster, PrimaryStorageClusterRefVO ref" + " where ref.clusterUuid = cluster.uuid" + " and ref.primaryStorageUuid = :uuid"; TypedQuery<ClusterVO> q = dbf.getEntityManager().createQuery(sql, ClusterVO.class); q.setParameter("uuid", self.getUuid()); List<ClusterVO> clusters = q.getResultList(); if (clusters.isEmpty()) { return new ArrayList<>(); } Map<String, FactoryCluster> m = new HashMap<>(); for (ClusterVO c : clusters) { FactoryCluster fc = m.get(c.getHypervisorType()); if (fc == null) { fc = new FactoryCluster(); fc.factory = getHypervisorBackendFactory(c.getHypervisorType()); fc.clusters = new ArrayList<>(); m.put(c.getHypervisorType(), fc); } fc.clusters.add(ClusterInventory.valueOf(c)); } List<FactoryCluster> fcs = new ArrayList<>(); fcs.addAll(m.values()); return fcs; } @Override protected void connectHook(final ConnectParam param, final Completion completion) { FlowChain chain = FlowChainBuilder.newShareFlowChain(); chain.setName(String.format("sync-capacity-of-local-storage-primary-storage-%s", self.getUuid())); chain.then(new ShareFlow() { Long totalPhysicalSize; Long availablePhysicalSize; Long volumeUsage; Long snapshotUsage; @Override public void setup() { flow(new NoRollbackFlow() { String __name__ = "reconnect-all-host-related-to-specified-local-storage"; @Override public void run(FlowTrigger trigger, Map data) { List<String> hostUuids; SimpleQuery<LocalStorageHostRefVO> q = dbf.createQuery(LocalStorageHostRefVO.class); q.select(LocalStorageHostRefVO_.hostUuid); q.add(LocalStorageHostRefVO_.primaryStorageUuid, Op.EQ, self.getUuid()); hostUuids = q.listValue(); if (hostUuids == null || hostUuids.isEmpty()) { trigger.next(); return; } List<ReconnectHostMsg> rmsgs = CollectionUtils.transformToList(hostUuids, new Function<ReconnectHostMsg, String>() { @Override public ReconnectHostMsg call(String hostUuid) { ReconnectHostMsg rmsg = new ReconnectHostMsg(); rmsg.setHostUuid(hostUuid); bus.makeTargetServiceIdByResourceUuid(rmsg, HostConstant.SERVICE_ID, hostUuid); return rmsg; } }); bus.send(rmsgs, new CloudBusListCallBack(trigger) { @Override public void run(List<MessageReply> replies) { for (MessageReply reply : replies) { if (!reply.isSuccess()) { logger.debug(String.format("failed to reconnect host[uuid:%s] because %s", self.getUuid(), reply.getError())); } } trigger.next(); } }); } }); done(new FlowDoneHandler(completion) { @Override public void handle(Map data) { RecalculatePrimaryStorageCapacityMsg rmsg = new RecalculatePrimaryStorageCapacityMsg(); rmsg.setPrimaryStorageUuid(self.getUuid()); bus.makeLocalServiceId(rmsg, PrimaryStorageConstant.SERVICE_ID); bus.send(rmsg); completion.success(); } }); error(new FlowErrorHandler(completion) { @Override public void handle(ErrorCode errCode, Map data) { completion.fail(errCode); } }); } }); chain.start(); } @Override protected void pingHook(Completion completion) { completion.success(); } @Override final protected void syncPhysicalCapacity(final ReturnValueCompletion<PhysicalCapacityUsage> completion) { final List<FactoryCluster> fs = getAllFactoriesForAttachedClusters(); class Sync { long total = 0; long avail = 0; Iterator<FactoryCluster> it = fs.iterator(); void sync() { if (!it.hasNext()) { PhysicalCapacityUsage ret = new PhysicalCapacityUsage(); ret.totalPhysicalSize = total; ret.availablePhysicalSize = avail; completion.success(ret); return; } FactoryCluster fc = it.next(); LocalStorageHypervisorBackend bkd = fc.factory.getHypervisorBackend(self); bkd.syncPhysicalCapacityInCluster(fc.clusters, new ReturnValueCompletion<PhysicalCapacityUsage>(completion) { @Override public void success(PhysicalCapacityUsage returnValue) { total += returnValue.totalPhysicalSize; avail += returnValue.availablePhysicalSize; sync(); } @Override public void fail(ErrorCode errorCode) { completion.fail(errorCode); } }); } } new Sync().sync(); } protected LocalStorageHypervisorFactory getHypervisorBackendFactoryByHostUuid(String hostUuid) { SimpleQuery<HostVO> q = dbf.createQuery(HostVO.class); q.select(HostVO_.hypervisorType); q.add(HostVO_.uuid, Op.EQ, hostUuid); String hvType = q.findValue(); return getHypervisorBackendFactory(hvType); } @Transactional(readOnly = true) private LocalStorageHypervisorFactory getHypervisorBackendFactoryByResourceUuid(String resUuid, String resourceType) { String sql = "select host.hypervisorType" + " from HostVO host, LocalStorageResourceRefVO ref" + " where ref.hostUuid = host.uuid" + " and ref.resourceUuid = :resUuid" + " and ref.primaryStorageUuid = :puuid"; TypedQuery<String> q = dbf.getEntityManager().createQuery(sql, String.class); q.setParameter("resUuid", resUuid); q.setParameter("puuid", self.getUuid()); List<String> ret = q.getResultList(); if (ret.isEmpty()) { throw new CloudRuntimeException( String.format("resource[uuid:%s, type: %s] is not on the local primary storage[uuid:%s]", resUuid, resourceType, self.getUuid())); } if (ret.size() != 1) { throw new CloudRuntimeException( String.format("resource[uuid:%s, type: %s] on the local primary storage[uuid:%s] maps to multiple hypervisor%s", resUuid, resourceType, self.getUuid(), ret)); } String hvType = ret.get(0); return getHypervisorBackendFactory(hvType); } private LocalStorageHypervisorFactory getHypervisorBackendFactory(String hvType) { if (hvType == null) { throw new CloudRuntimeException("hvType is null!!!"); } for (LocalStorageHypervisorFactory f : pluginRgty.getExtensionList(LocalStorageHypervisorFactory.class)) { if (hvType.equals(f.getHypervisorType())) { return f; } } throw new CloudRuntimeException(String.format("cannot find LocalStorageHypervisorFactory with hypervisorType[%s]", hvType)); } @Override public void attachHook(final String clusterUuid, Completion completion) { SimpleQuery<ClusterVO> q = dbf.createQuery(ClusterVO.class); q.select(ClusterVO_.hypervisorType); q.add(ClusterVO_.uuid, Op.EQ, clusterUuid); String hvType = q.findValue(); LocalStorageHypervisorFactory f = getHypervisorBackendFactory(hvType); LocalStorageHypervisorBackend bkd = f.getHypervisorBackend(self); bkd.attachHook(clusterUuid, completion); } @Override protected void checkImageIfNeedToDownload(DownloadIsoToPrimaryStorageMsg msg){ logger.debug("check if image exist in disabled primary storage"); if(self.getState() != PrimaryStorageState.Disabled){ return ; } if( !Q.New(ImageCacheVO.class) .eq(ImageCacheVO_.primaryStorageUuid, self.getUuid()) .eq(ImageCacheVO_.imageUuid, msg.getIsoSpec().getInventory().getUuid()) .like(ImageCacheVO_.installUrl, String.format("%%hostUuid://%s%%", msg.getDestHostUuid())) .isExists()){ throw new OperationFailureException(errf.stringToOperationError( String.format("cannot attach ISO to a primary storage[uuid:%s] which is disabled", self.getUuid()))); } } }