package com.sungardas.init; import com.amazonaws.auth.InstanceProfileCredentialsProvider; import com.amazonaws.regions.Region; import com.amazonaws.regions.Regions; import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig; import com.amazonaws.services.dynamodbv2.datamodeling.IDynamoDBMapper; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.GetObjectRequest; import com.amazonaws.services.s3.model.S3Object; import com.sungardas.enhancedsnapshots.aws.AmazonConfigProvider; import com.sungardas.enhancedsnapshots.aws.dynamodb.model.*; import com.sungardas.enhancedsnapshots.exception.EnhancedSnapshotsException; import com.sungardas.enhancedsnapshots.service.upgrade.SystemUpgrade; import com.sungardas.enhancedsnapshots.service.upgrade.UpgradeSystemTo003; import com.sungardas.enhancedsnapshots.util.SystemUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.codehaus.jackson.map.ObjectMapper; import org.springframework.beans.factory.annotation.Value; import org.w3c.dom.Document; import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; public class SystemRestoreServiceImpl implements SystemRestoreService { private static final Logger LOG = LogManager.getLogger(SystemRestoreServiceImpl.class); private static final String TEMP_DIRECTORY_PREFIX = "systemBackupFiles"; private static final String INFO_FILE_NAME = "info"; private static final String VERSION_KEY = "version"; private static final String TEMP_FILE_SUFFIX = "ZIP"; @Value("${enhancedsnapshots.saml.sp.cert.jks}") private String samlCertJks; @Value("${enhancedsnapshots.saml.idp.metadata}") private String samlIdpMetadata; @Value("${enhancedsnapshots.default.sdfs.backup.file.name}") private String backupZipName; private Configuration currentConfiguration; private SystemUpgrade systemUpgrade; private final ObjectMapper objectMapper = new ObjectMapper(); private IDynamoDBMapper dynamoDBMapper; private AmazonS3 amazonS3; private void init() { InstanceProfileCredentialsProvider credentialsProvider = new InstanceProfileCredentialsProvider(); AmazonDynamoDB amazonDynamoDB = new AmazonDynamoDBClient(credentialsProvider); amazonDynamoDB.setRegion(Regions.getCurrentRegion()); dynamoDBMapper = new DynamoDBMapper(amazonDynamoDB, dynamoDBMapperConfig()); amazonS3 = new AmazonS3Client(credentialsProvider); Region current = Regions.getCurrentRegion(); if (!current.equals(Region.getRegion(Regions.US_EAST_1))) { amazonS3.setRegion(current); } systemUpgrade = new UpgradeSystemTo003(); } public DynamoDBMapperConfig dynamoDBMapperConfig() { DynamoDBMapperConfig.Builder builder = new DynamoDBMapperConfig.Builder(); builder.withTableNameOverride(DynamoDBMapperConfig.TableNameOverride. withTableNamePrefix(AmazonConfigProvider.getDynamoDbPrefix())); return builder.build(); } public void restore(String bucketName) { try { init(); LOG.info("System restore started"); Path tempDirectory = Files.createTempDirectory(TEMP_DIRECTORY_PREFIX); LOG.info("Download from S3"); downloadFromS3(tempDirectory, bucketName); systemUpgrade.upgrade(tempDirectory, getBackupVersion(tempDirectory)); LOG.info("Restore DB"); restoreDB(tempDirectory); LOG.info("Restore files"); restoreFiles(tempDirectory); LOG.info("Restore SDFS state"); restoreSDFS(tempDirectory); // restore SSO files if exist if(currentConfiguration.isSsoLoginMode()) { LOG.info("Restoring saml certificate and ipd metadata", 90); restoreSSOFiles(tempDirectory); } } catch (Exception e) { LOG.error("System restore failed"); LOG.error(e); throw new EnhancedSnapshotsException(e); } } /** * Method for defining application version, which created system backup * * @param tempDirectory directory to which was unzipped system backup * @return application version */ private String getBackupVersion(final Path tempDirectory) { Path infoFile = Paths.get(tempDirectory.toString(), INFO_FILE_NAME); if (infoFile.toFile().exists()) { try (FileInputStream fileInputStream = new FileInputStream(infoFile.toFile())) { HashMap<String, String> info = objectMapper.readValue(fileInputStream, HashMap.class); if (info.containsKey(VERSION_KEY)) { return info.get(VERSION_KEY); } else { LOG.error("Invalid info file formant"); throw new EnhancedSnapshotsException("Invalid info file formant"); } } catch (IOException e) { LOG.error("Failed to parse info file"); LOG.error(e); throw new EnhancedSnapshotsException(e); } } return "0.0.1"; } private void restoreDB(Path tempDirectory) throws IOException { restoreConfiguration(tempDirectory); restoreTable(BackupEntry.class, tempDirectory); restoreTable(RetentionEntry.class, tempDirectory); restoreTable(SnapshotEntry.class, tempDirectory); restoreTable(User.class, tempDirectory); currentConfiguration = dynamoDBMapper.load(Configuration.class, SystemUtils.getSystemId()); } private void restoreSDFS(final Path tempDirectory) throws IOException, ParserConfigurationException, SAXException, XPathExpressionException { restoreFile(tempDirectory, Paths.get(currentConfiguration.getSdfsConfigPath())); if (currentConfiguration.isClusterMode() && (currentConfiguration.getChunkStoreIV() == null || currentConfiguration.getChunkStoreEncryptionKey() == null)) { DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); File sdfsConfig = new File(currentConfiguration.getSdfsConfigPath()); Document document = documentBuilder.parse(sdfsConfig); XPathFactory xPathFactory = XPathFactory.newInstance(); XPath ivXPath = xPathFactory.newXPath(); XPath keyXPath = xPathFactory.newXPath(); XPathExpression ivExpression = ivXPath.compile("/subsystem-config/local-chunkstore/@encryption-iv"); XPathExpression keyExpression = keyXPath.compile("/subsystem-config/local-chunkstore/@encryption-key"); currentConfiguration.setChunkStoreIV(ivExpression.evaluate(document)); currentConfiguration.setChunkStoreEncryptionKey(keyExpression.evaluate(document)); currentConfiguration.setSdfsCliPsw(SystemUtils.getSystemId()); dynamoDBMapper.save(currentConfiguration); sdfsConfig.delete(); } } private void restoreFile(Path tempDirectory, Path destPath) throws IOException { Path fileName = destPath.getFileName(); Files.copy(Paths.get(tempDirectory.toString(), fileName.toString()), destPath, StandardCopyOption.REPLACE_EXISTING); } private void restoreFiles(Path tempDirectory) { //nginx certificates try { restoreFile(tempDirectory, Paths.get(currentConfiguration.getNginxCertPath())); restoreFile(tempDirectory, Paths.get(currentConfiguration.getNginxKeyPath())); } catch (IOException e) { LOG.warn("Nginx certificate not found"); } } private void restoreSSOFiles(Path tempDirectory) { try { restoreFile(tempDirectory, Paths.get(System.getProperty("catalina.home"), samlCertJks)); restoreFile(tempDirectory, Paths.get(System.getProperty("catalina.home"), samlIdpMetadata)); } catch (IOException e) { LOG.warn("Nginx certificate not found"); } } private void downloadFromS3(Path tempDirectory, String bucketName) throws IOException { // download LOG.info("-Download"); GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, backupZipName); S3Object s3object = amazonS3.getObject(getObjectRequest); Path tempFile = Files.createTempFile(TEMP_DIRECTORY_PREFIX, TEMP_FILE_SUFFIX); Files.copy(s3object.getObjectContent(), tempFile, StandardCopyOption.REPLACE_EXISTING); LOG.info(" -Unzip"); //unzip try (FileInputStream fileInputStream = new FileInputStream(tempFile.toFile()); ZipInputStream zipInputStream = new ZipInputStream(fileInputStream)) { ZipEntry entry; while ((entry = zipInputStream.getNextEntry()) != null) { // in case entry is directory system backup relates to version 0.0.1 // copy /etc/sdfs/awspool-volume-cfg.xml to temp dir if(entry.isDirectory()){ zipInputStream.getNextEntry(); zipInputStream.getNextEntry(); Path dest = Paths.get(tempDirectory.toString(), "awspool-volume-cfg.xml"); Files.copy(zipInputStream, dest, StandardCopyOption.REPLACE_EXISTING); break; } Path dest = Paths.get(tempDirectory.toString(), entry.getName()); Files.copy(zipInputStream, dest, StandardCopyOption.REPLACE_EXISTING); } } //cleanup tempFile.toFile().delete(); } private void restoreTable(Class tableClass, Path tempDirectory) throws IOException { LOG.info(" -Restore table: {}", tableClass.getSimpleName()); File src = Paths.get(tempDirectory.toString(), tableClass.getName()).toFile(); try (FileInputStream fileInputStream = new FileInputStream(src)) { ArrayList data = objectMapper.readValue(fileInputStream, objectMapper.getTypeFactory().constructCollectionType(List.class, tableClass)); dynamoDBMapper.batchSave(data); } catch (IOException e) { LOG.warn("Table restore failed: {}", e.getLocalizedMessage()); } } private void restoreConfiguration(final Path tempDirectory) { File src = Paths.get(tempDirectory.toString(), Configuration.class.getName()).toFile(); try (FileInputStream fileInputStream = new FileInputStream(src)) { ArrayList<Configuration> data = objectMapper.readValue(fileInputStream, objectMapper.getTypeFactory().constructCollectionType(List.class, Configuration.class)); if (!data.isEmpty()) { Configuration configuration = data.get(0); configuration.setConfigurationId(SystemUtils.getSystemId()); } dynamoDBMapper.batchSave(data); } catch (IOException e) { LOG.warn("Table restore failed: {}", e.getLocalizedMessage()); } } }