From 3286e6edba9230a256a00ada54501f54d3744dfc Mon Sep 17 00:00:00 2001 From: Anthony Xu Date: Wed, 3 Oct 2012 19:49:27 -0700 Subject: [PATCH] Bug : 15133 only delete snapshot which is not referenced by any snapshots in DB for a volume --- .../api/CleanupSnapshotBackupCommand.java | 9 ++ .../resource/NfsSecondaryStorageResource.java | 116 +++++++++++++++--- .../com/cloud/storage/StorageManagerImpl.java | 9 +- 3 files changed, 114 insertions(+), 20 deletions(-) diff --git a/api/src/com/cloud/agent/api/CleanupSnapshotBackupCommand.java b/api/src/com/cloud/agent/api/CleanupSnapshotBackupCommand.java index 5c168fedf8f..389c2c03b68 100644 --- a/api/src/com/cloud/agent/api/CleanupSnapshotBackupCommand.java +++ b/api/src/com/cloud/agent/api/CleanupSnapshotBackupCommand.java @@ -19,11 +19,14 @@ package com.cloud.agent.api; import java.util.List; +import com.cloud.hypervisor.Hypervisor.HypervisorType; + public class CleanupSnapshotBackupCommand extends Command { private String secondaryStoragePoolURL; private Long dcId; private Long accountId; private Long volumeId; + private HypervisorType type; private List validBackupUUIDs; protected CleanupSnapshotBackupCommand() { @@ -40,12 +43,14 @@ public class CleanupSnapshotBackupCommand extends Command { Long dcId, Long accountId, Long volumeId, + HypervisorType type, List validBackupUUIDs) { this.secondaryStoragePoolURL = secondaryStoragePoolURL; this.dcId = dcId; this.accountId = accountId; this.volumeId = volumeId; + this.type = type; this.validBackupUUIDs = validBackupUUIDs; } @@ -57,6 +62,10 @@ public class CleanupSnapshotBackupCommand extends Command { return dcId; } + public HypervisorType getType() { + return type; + } + public Long getAccountId() { return accountId; } diff --git a/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java b/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java index 46a31055296..6cb649f10bf 100755 --- a/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java +++ b/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java @@ -17,6 +17,7 @@ */ package com.cloud.storage.resource; +import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; @@ -30,6 +31,7 @@ import java.security.NoSuchAlgorithmException; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; @@ -70,6 +72,7 @@ import com.cloud.agent.api.storage.DownloadProgressCommand; import com.cloud.agent.api.storage.UploadCommand; import com.cloud.host.Host; import com.cloud.host.Host.Type; +import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.resource.ServerResourceBase; import com.cloud.storage.StorageLayer; import com.cloud.storage.template.DownloadManager; @@ -83,6 +86,7 @@ import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; import com.cloud.utils.net.NfsUtils; +import com.cloud.utils.script.OutputInterpreter; import com.cloud.utils.script.Script; import com.cloud.vm.SecondaryStorageVm; @@ -405,6 +409,44 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S return new Answer(cmd, true, null); } + public static class VhdChainParser extends OutputInterpreter { + private List> vhdchains = new ArrayList>(); + + + public VhdChainParser() { + } + + @Override + public String interpret(BufferedReader reader) throws IOException { + String line = null; + List vhdchain = new ArrayList(); + while ((line = reader.readLine()) != null) { + s_logger.trace("VHD " + line); + if(line.startsWith("vhd=") && !vhdchain.isEmpty()) { + vhdchains.add(vhdchain); + s_logger.trace("VHD CHAINS " + vhdchains); + vhdchain = new ArrayList(); + } + vhdchain.add(line.trim().substring(4, 40)); + s_logger.trace("VHD CHAIN " + vhdchain); + } + if (!vhdchain.isEmpty()) { + vhdchains.add(vhdchain); + } + s_logger.trace("VHD CHAINS " + vhdchains); + return null; + } + + public List> getVhdChains() { + return vhdchains; + } + + @Override + public boolean drain() { + return true; + } + } + Answer execute(CleanupSnapshotBackupCommand cmd) { String parent = getRootDir(cmd.getSecondaryStoragePoolURL()); if (!parent.endsWith(File.separator)) { @@ -413,22 +455,64 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S String absoluteSnapsthotDir = parent + File.separator + "snapshots" + File.separator + cmd.getAccountId() + File.separator + cmd.getVolumeId(); File ssParent = new File(absoluteSnapsthotDir); if (ssParent.exists() && ssParent.isDirectory()) { - File[] files = ssParent.listFiles(); - for (File file : files) { - boolean found = false; - String filename = file.getName(); - for (String uuid : cmd.getValidBackupUUIDs()) { - if (filename.startsWith(uuid)) { - found = true; - break; - } - } - if (!found) { - file.delete(); - String msg = "snapshot " + filename + " is not recorded in DB, remove it"; - s_logger.warn(msg); - } - } + if (cmd.getType() == HypervisorType.XenServer ) { + Script command = new Script("/bin/bash", s_logger); + command.add("-c"); + command.add("/bin/vhd-util scan -p " + absoluteSnapsthotDir + "/*.vhd"); + VhdChainParser vhdChainParser = new VhdChainParser(); + String result = command.execute(vhdChainParser); + if (result != null) { + s_logger.warn("Can not parser VHD chain for snapshots of volume " + cmd.getVolumeId() ); + } else { + List> vhdChains = vhdChainParser.getVhdChains(); + s_logger.trace("VHD CHAINS " + vhdChains); + for (String uuid : cmd.getValidBackupUUIDs()) { + Iterator> it = vhdChains.iterator(); + while (it.hasNext()) { + List vhdchain = it.next(); + s_logger.trace("VHD CHAIN " + vhdchain); + if (vhdchain == null || vhdchain.isEmpty()) { + continue; + } + if (vhdchain.contains(uuid)) { + s_logger.trace("VHD keep vhd chain " + vhdchain); + it.remove(); + } + } + } + s_logger.trace("VHD CHAINS " + vhdChains); + for (List vhdchain : vhdChains) { + if (vhdchain == null || vhdchain.isEmpty()) { + continue; + } + for (String uuid : vhdchain) { + File file = new File(absoluteSnapsthotDir + "/" + + uuid + ".vhd"); + file.delete(); + String msg = "snapshot " + file.getName() + " is removed"; + s_logger.warn(msg); + } + } + + } + } else { + File[] files = ssParent.listFiles(); + for (File file : files) { + boolean found = false; + String filename = file.getName(); + for (String uuid : cmd.getValidBackupUUIDs()) { + if (filename.startsWith(uuid)) { + found = true; + break; + } + } + if (!found) { + file.delete(); + String msg = "snapshot " + filename + " is not recorded in DB, remove it"; + s_logger.warn(msg); + } + } + } } return new Answer(cmd, true, null); } diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index 0bb44a6b05a..2251330bcb5 100755 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -1063,9 +1063,7 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag @Override public boolean start() { if (_storageCleanupEnabled) { - Random generator = new Random(); - int initialDelay = generator.nextInt(_storageCleanupInterval); - _executor.scheduleWithFixedDelay(new StorageGarbageCollector(), initialDelay, _storageCleanupInterval, TimeUnit.SECONDS); + _executor.scheduleWithFixedDelay(new StorageGarbageCollector(), _storageCleanupInterval, _storageCleanupInterval, TimeUnit.SECONDS); } else { s_logger.debug("Storage cleanup is not enabled, so the storage cleanup thread is not being scheduled."); } @@ -2130,8 +2128,11 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag if (snapshots == null) { continue; } + + HypervisorType type = _clusterDao.findById(_storagePoolDao.findById(volume.getPoolId()).getClusterId()).getHypervisorType(); + CleanupSnapshotBackupCommand cmd = new CleanupSnapshotBackupCommand(secondaryStorageHost.getStorageUrl(), secondaryStorageHost.getDataCenterId(), volume.getAccountId(), - volumeId, snapshots); + volumeId, type, snapshots); Answer answer = _agentMgr.sendToSecStorage(secondaryStorageHost, cmd); if ((answer == null) || !answer.getResult()) {