package org.zstack.storage.primary.local; import org.springframework.transaction.annotation.Transactional; import org.zstack.core.cloudbus.CloudBusCallBack; import org.zstack.core.thread.SyncTask; import org.zstack.header.host.HostVO; import org.zstack.header.managementnode.ManagementNodeReadyExtensionPoint; import org.zstack.header.message.MessageReply; import org.zstack.header.storage.primary.ImageCacheShadowVO; import org.zstack.header.storage.primary.ImageCacheVO; import org.zstack.header.storage.primary.PrimaryStorageConstant; import org.zstack.header.volume.VolumeVO; import org.zstack.storage.primary.ImageCacheCleaner; import org.zstack.storage.primary.local.LocalStorageKvmBackend.CacheInstallPath; import org.zstack.utils.CollectionUtils; import org.zstack.utils.Utils; import org.zstack.utils.function.Function; import org.zstack.utils.logging.CLogger; import javax.persistence.TypedQuery; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Created by xing5 on 2016/7/20. */ public class LocalStorageImageCleaner extends ImageCacheCleaner implements ManagementNodeReadyExtensionPoint { private static final CLogger logger = Utils.getLogger(LocalStorageImageCleaner.class); @Override public void managementNodeReady() { startGC(); } @Override protected String getPrimaryStorageType() { return LocalStorageConstants.LOCAL_STORAGE_TYPE; } @Transactional protected List<ImageCacheShadowVO> createShadowImageCacheVOsForNewDeletedAndOld(String psUUid) { List<Long> staleImageCacheIds = getStaleImageCacheIds(psUUid); if (staleImageCacheIds == null || staleImageCacheIds.isEmpty()) { return null; } String sql = "select c from ImageCacheVO c where c.id in (:ids)"; TypedQuery<ImageCacheVO> cq = dbf.getEntityManager().createQuery(sql, ImageCacheVO.class); cq.setParameter("ids", staleImageCacheIds); List<ImageCacheVO> deleted = cq.getResultList(); Map<String, List<ImageCacheVO>> refMap = new HashMap<String, List<ImageCacheVO>>(); for (ImageCacheVO c : deleted) { CacheInstallPath p = new CacheInstallPath(); p.fullPath = c.getInstallUrl(); p.disassemble(); String hostUuid = p.hostUuid; List<ImageCacheVO> refs = refMap.get(hostUuid); if (refs == null) { refs = new ArrayList<ImageCacheVO>(); refMap.put(hostUuid, refs); } refs.add(c); } List<ImageCacheVO> stale = new ArrayList<ImageCacheVO>(); for (Map.Entry<String, List<ImageCacheVO>> e : refMap.entrySet()) { String hostUuid = e.getKey(); List<ImageCacheVO> refs = e.getValue(); List<Long> cacheIds = CollectionUtils.transformToList(refs, new Function<Long, ImageCacheVO>() { @Override public Long call(ImageCacheVO arg) { return arg.getId(); } }); sql = "select c from ImageCacheVO c where c.imageUuid not in (select vol.rootImageUuid from VolumeVO vol, LocalStorageResourceRefVO ref" + " where vol.uuid = ref.resourceUuid and ref.resourceType = :rtype and ref.hostUuid = :huuid and vol.rootImageUuid is not null) and c.id in (:ids)"; cq = dbf.getEntityManager().createQuery(sql, ImageCacheVO.class); cq.setParameter("rtype", VolumeVO.class.getSimpleName()); cq.setParameter("huuid", hostUuid); cq.setParameter("ids", cacheIds); stale.addAll(cq.getResultList()); } if (stale.isEmpty()) { return null; } logger.debug(String.format("found %s stale images in cache on the primary storage[type:%s], they are about to be cleaned up", stale.size(), getPrimaryStorageType())); for (ImageCacheVO vo : stale) { dbf.getEntityManager().persist(new ImageCacheShadowVO(vo)); dbf.getEntityManager().remove(vo); } sql = "select s from ImageCacheShadowVO s, PrimaryStorageVO p where p.uuid = s.primaryStorageUuid and p.type = :ptype"; TypedQuery<ImageCacheShadowVO> sq = dbf.getEntityManager().createQuery(sql, ImageCacheShadowVO.class); sq.setParameter("ptype", getPrimaryStorageType()); return sq.getResultList(); } protected void doCleanup(String psUuid) { List<ImageCacheShadowVO> shadowVOs = createShadowImageCacheVOs(psUuid); if (shadowVOs == null || shadowVOs.isEmpty()) { return; } for (final ImageCacheShadowVO vo : shadowVOs) { if (!destMaker.isManagedByUs(vo.getImageUuid())) { continue; } CacheInstallPath p = new CacheInstallPath(); p.fullPath = vo.getInstallUrl(); p.disassemble(); if (!dbf.isExist(p.hostUuid, HostVO.class)){ dbf.removeByPrimaryKey(vo.getId(), ImageCacheShadowVO.class); continue; } LocalStorageDeleteImageCacheOnPrimaryStorageMsg msg = new LocalStorageDeleteImageCacheOnPrimaryStorageMsg(); msg.setHostUuid(p.hostUuid); msg.setImageUuid(vo.getImageUuid()); msg.setInstallPath(p.installPath); msg.setPrimaryStorageUuid(vo.getPrimaryStorageUuid()); bus.makeTargetServiceIdByResourceUuid(msg, PrimaryStorageConstant.SERVICE_ID, vo.getPrimaryStorageUuid()); bus.send(msg, new CloudBusCallBack(null) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { logger.warn(String.format("failed to delete the stale image cache[%s] on the primary storage[%s], %s," + "will re-try later", vo.getInstallUrl(), vo.getPrimaryStorageUuid(), reply.getError())); return; } logger.debug(String.format("successfully deleted the stale image cache[%s] on the primary storage[%s]", vo.getInstallUrl(), vo.getPrimaryStorageUuid())); dbf.remove(vo); } }); } } @Override public void cleanup(String psUuid) { ImageCacheCleaner self = this; thdf.syncSubmit(new SyncTask<Void>() { @Override public Void call() throws Exception { doCleanup(psUuid); return null; } @Override public String getName() { return getSyncSignature(); } @Override public String getSyncSignature() { return self.getClass().getName(); } @Override public int getSyncLevel() { return 1; } }); } }