/* * Copyright (c) 2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.management.backup; import com.emc.storageos.coordinator.client.service.DrUtil; import com.emc.storageos.management.backup.exceptions.BackupException; import org.apache.cassandra.service.StorageService; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileNotFoundException; import java.io.FilenameFilter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.List; public class DbBackupHandler extends BackupHandler { private static final Logger log = LoggerFactory.getLogger(DbBackupHandler.class); public static final String DB_SNAPSHOT_SUBDIR = "snapshots"; public static final String DB_SSTABLE_TYPE = ".db"; private List<String> keyspaceList; private List<String> ignoreCfList; /** * Sets vipr keyspace name * * @param keyspaceList * The list of vipr keyspace */ public void setKeyspaceList(List<String> keyspaceList) { this.keyspaceList = keyspaceList; } /** * Gets keyspace list */ public List<String> getKeyspaceList() { return keyspaceList; } /** * Sets ignored column family list to include basic ignore column families, such as "Stats" * * @param ignoreCfList * The list of ignored column family */ public void setIgnoreCfList(List<String> ignoreCfList) { this.ignoreCfList = ignoreCfList; } /** * Gets ignore ColumnFamily list */ public List<String> getIgnoreCfList() { return ignoreCfList; } /** * Gets valid keyspace folder of ViPR DB or GeoDB */ public File getValidKeyspace(final String viprKeyspace) { log.debug("Searching ViPR keyspace {}...", viprKeyspace); for (String dataFolder : StorageService.instance.getAllDataFileLocations()) { File[] keyspaceList = new File(dataFolder).listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.trim().equals(viprKeyspace.trim()); } }); if (keyspaceList != null && keyspaceList.length == 1 && keyspaceList[0].isDirectory()) { log.debug("ViPR keyspace found at {}", keyspaceList[0]); return keyspaceList[0]; } } throw new IllegalArgumentException( String.format("Invalid ViPR keyspace name: %s", viprKeyspace)); } @Override public boolean isNeed() { // no any precheck here return true; } @Override public String createBackup(final String backupTag) { // For multi vdc ViPR, need to reinit geodb during restore, so use the special backup type // to show the difference if (backupType.equals(BackupType.geodb) && backupContext.isGeoEnv()) { backupType = BackupType.geodbmultivdc; } String fullBackupTag = backupTag + BackupConstants.BACKUP_NAME_DELIMITER + backupType.name() + BackupConstants.BACKUP_NAME_DELIMITER + backupContext.getNodeId() + BackupConstants.BACKUP_NAME_DELIMITER + backupContext.getNodeName(); checkBackupFileExist(backupTag, fullBackupTag); for (String viprKeyspace : getKeyspaceList()) { try { StorageService.instance.takeSnapshot(fullBackupTag, viprKeyspace); } catch (IOException ex) { clearSnapshot(fullBackupTag); throw BackupException.fatals.failedToTakeDbSnapshot(fullBackupTag, viprKeyspace, ex); } } log.info("DB snapshot ({}) has been taken successfully.", fullBackupTag); return fullBackupTag; } @Override public File dumpBackup(final String backupTag, final String fullBackupTag) { // Prepares backup folder to accept snapshot files File targetDir = new File(backupContext.getBackupDir(), backupTag); if (!targetDir.exists()) { targetDir.mkdir(); } File backupFolder = new File(targetDir, fullBackupTag); if (backupFolder.exists()) { FileUtils.deleteQuietly(backupFolder); } backupFolder.mkdir(); for (String keyspace : getKeyspaceList()) { try { File ksBackupFolder = new File(backupFolder, keyspace); ksBackupFolder.mkdir(); File[] cfDirs = getValidKeyspace(keyspace).listFiles(); cfDirs = (cfDirs == null) ? BackupConstants.EMPTY_ARRAY : cfDirs; for (File cfDir : cfDirs) { File cfBackupFolder = new File(ksBackupFolder, cfDir.getName()); File snapshotFolder = new File(cfDir, DB_SNAPSHOT_SUBDIR + File.separator + fullBackupTag); // Filters ignored Column Family if (ignoreCfList != null) { String cfName = cfDir.getName(); if (cfDir.getName().contains(BackupConstants.CASSANDRA_CF_NAME_DELIMITER)) { cfName = cfDir.getName().split(BackupConstants.CASSANDRA_CF_NAME_DELIMITER)[0]; } if (ignoreCfList.contains(cfName)) { FileUtils.deleteQuietly(snapshotFolder); cfBackupFolder.mkdir(); continue; } } if (!snapshotFolder.exists()) { // Handles stale Column Family String[] cfSubFileList = cfDir.list(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.endsWith(DB_SSTABLE_TYPE); } }); if (cfSubFileList == null || cfSubFileList.length == 0) { log.info("Stale empty cf foler: {}", cfDir.getName()); } else { log.warn("No snapshot created for cf: {}", cfDir.getName()); } cfBackupFolder.mkdir(); continue; } // Moves snapshot folder and renames it with Column Family name Files.move(snapshotFolder.toPath(), cfBackupFolder.toPath(), StandardCopyOption.REPLACE_EXISTING); } } catch (IOException ex) { clearSnapshot(fullBackupTag); throw BackupException.fatals.failedToDumpDbSnapshot(fullBackupTag, keyspace, ex); } } log.info("DB snapshot files have been moved to ({}) successfully.", backupFolder.getAbsolutePath()); return backupFolder; } private void clearSnapshot(final String fullBackupTag) { for (String viprKeyspace : getKeyspaceList()) { try { StorageService.instance.clearSnapshot(fullBackupTag, viprKeyspace); } catch (IOException ignore) { log.error("Failed to clear DB snapshot: {}, {}", fullBackupTag, ignore.getMessage()); } } } }