/* This file is part of VoltDB. * Copyright (C) 2008-2017 VoltDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with VoltDB. If not, see <http://www.gnu.org/licenses/>. */ package org.voltdb.settings; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.nio.file.Files; import java.util.List; import java.util.Map; import java.util.NavigableMap; import java.util.Properties; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.voltdb.common.Constants; import org.voltdb.utils.MiscUtils; import org.voltdb.utils.VoltFile; import org.aeonbits.owner.Config.Sources; import org.aeonbits.owner.ConfigFactory; import com.google_voltpatches.common.collect.ImmutableList; import com.google_voltpatches.common.collect.ImmutableMap; import com.google_voltpatches.common.collect.ImmutableSet; import com.google_voltpatches.common.collect.ImmutableSortedMap; @Sources({"file:${org.voltdb.config.dir}/path.properties", "file:${org.voltdb.config.dir}/../.paths"}) public interface NodeSettings extends Settings { public final static String CL_SNAPSHOT_PATH_KEY = "org.voltdb.path.command_log_snapshot"; public final static String CL_PATH_KEY = "org.voltdb.path.command_log"; public final static String SNAPTHOT_PATH_KEY = "org.voltdb.path.snapshots"; public final static String VOLTDBROOT_PATH_KEY = "org.voltdb.path.voltdbroot"; public final static String EXPORT_OVERFLOW_PATH_KEY = "org.voltdb.path.export_overflow"; public final static String DR_OVERFLOW_PATH_KEY = "org.voltdb.path.dr_overflow"; public final static String LOCAL_SITES_COUNT_KEY = "org.voltdb.local_sites_count"; @Key(VOLTDBROOT_PATH_KEY) public VoltFile getVoltDBRoot(); @Key(CL_PATH_KEY) public File getCommandLog(); @Key(CL_SNAPSHOT_PATH_KEY) public File getCommandLogSnapshot(); @Key(SNAPTHOT_PATH_KEY) public File getSnapshoth(); @Key(EXPORT_OVERFLOW_PATH_KEY) public File getExportOverflow(); @Key(DR_OVERFLOW_PATH_KEY) public File getDROverflow(); @Key(LOCAL_SITES_COUNT_KEY) @DefaultValue("8") public int getLocalSitesCount(); public static NodeSettings create(Map<?, ?>...imports) { return ConfigFactory.create(NodeSettings.class, imports); } default File resolve(File path) { return path.isAbsolute() ? path : new File(getVoltDBRoot(), path.getPath()); } default NavigableMap<String, File> getManagedArtifactPaths() { return ImmutableSortedMap.<String, File>naturalOrder() .put(CL_PATH_KEY, resolve(getCommandLog())) .put(CL_SNAPSHOT_PATH_KEY, resolve(getCommandLogSnapshot())) .put(SNAPTHOT_PATH_KEY, resolve(getSnapshoth())) .put(EXPORT_OVERFLOW_PATH_KEY, resolve(getExportOverflow())) .put(DR_OVERFLOW_PATH_KEY, resolve(getDROverflow())) .build(); } default boolean archiveSnapshotDirectory() { File snapshotDH = resolve(getSnapshoth()); String [] snapshots = snapshotDH.list(); if (snapshots == null || snapshots.length == 0) { return false; } Pattern archvRE = Pattern.compile(getSnapshoth().getName() + "\\.(\\d+)"); final ImmutableSortedMap.Builder<Integer, File> mb = ImmutableSortedMap.naturalOrder(); File parent = snapshotDH.getParentFile(); parent.listFiles(new FileFilter() { @Override public boolean accept(File path) { Matcher mtc = archvRE.matcher(path.getName()); if (path.isDirectory() && mtc.matches()) { mb.put(Integer.parseInt(mtc.group(1)), path); return true; } return false; } }); NavigableMap<Integer, File> snapdirs = mb.build(); for (Map.Entry<Integer, File> e: snapdirs.descendingMap().entrySet()) { File renameTo = new File(snapshotDH.getPath() + "." + (e.getKey() + 1)); try { Files.move(e.getValue().toPath(), renameTo.toPath()); } catch (IOException exc) { throw new SettingsException("failed to rename " + e.getValue() + " to " + renameTo, exc); } } File renameTo = new File(snapshotDH.getPath() + ".1"); try { Files.move(snapshotDH.toPath(), renameTo.toPath()); } catch (IOException e) { throw new SettingsException("failed to rename " + snapshotDH + " to " + renameTo, e); } if (!snapshotDH.mkdir()) { throw new SettingsException("failed to create snapshot directory " + snapshotDH); } return true; } default List<String> ensureDirectoriesExist() { ImmutableList.Builder<String> failed = ImmutableList.builder(); Map<String, File> managedArtifactsPaths = getManagedArtifactPaths(); File configDH = resolve(new File(Constants.CONFIG_DIR)); File logDH = resolve(new File("log")); for (File path: managedArtifactsPaths.values()) { if (!path.exists() && !path.mkdirs()) { failed.add("Unable to create directory \"" + path + "\""); } if (!path.isDirectory()) { failed.add("Specified path \"" + path + "\" is not a directory"); } if ( !path.canRead() || !path.canWrite() || !path.canExecute()) { failed.add("Directory \"" + path + "\" is not read, write, execute (rwx) accessible"); } if (logDH.equals(path) || configDH.equals(path)) { failed.add("\"" + path + "\" is a reserved directory name"); } } Set<File> distinct = ImmutableSet.<File>builder() .addAll(managedArtifactsPaths.values()) .add(getVoltDBRoot()) .build(); if (distinct.size() < (managedArtifactsPaths.size() + 1)) { failed.add("Managed path values \"" + managedArtifactsPaths + "\" are not distinct"); } return failed.build(); } default boolean clean() { boolean archivedSnapshots = archiveSnapshotDirectory(); for (File path: getManagedArtifactPaths().values()) { File [] children = path.listFiles(); if (children == null) continue; for (File child: children) { MiscUtils.deleteRecursively(child); } } return archivedSnapshots; } @Override default Properties asProperties() { ImmutableMap.Builder<String, String> mb = ImmutableMap.builder(); try { mb.put(VOLTDBROOT_PATH_KEY, getVoltDBRoot().getCanonicalPath()); for (Map.Entry<String, File> e: getManagedArtifactPaths().entrySet()) { mb.put(e.getKey(), e.getValue().getCanonicalPath()); } mb.put(LOCAL_SITES_COUNT_KEY, Integer.toString(getLocalSitesCount())); } catch (IOException e) { throw new SettingsException("failed to canonicalize" + this); } Properties props = new Properties(); props.putAll(mb.build()); return props; } default void store() { File configFH = new File(Settings.getConfigDir(), "path.properties"); store(configFH, "VoltDB path settings. DO NOT MODIFY THIS FILE!"); File deprectedConfigFH = new File(getVoltDBRoot(),".paths"); if (deprectedConfigFH.exists()) deprectedConfigFH.delete(); } }