blob: 8b30b85923a2a9d4ab24096e78845109460e2fd0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.geode.management.internal.cli.commands;
import static org.apache.geode.management.internal.cli.i18n.CliStrings.GROUP;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Properties;
import java.util.Set;
import joptsimple.internal.Strings;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.logging.log4j.Logger;
import org.springframework.shell.core.annotation.CliCommand;
import org.springframework.shell.core.annotation.CliOption;
import org.apache.geode.distributed.ConfigurationPersistenceService;
import org.apache.geode.distributed.internal.InternalConfigurationPersistenceService;
import org.apache.geode.internal.logging.LogService;
import org.apache.geode.management.cli.CliMetaData;
import org.apache.geode.management.cli.GfshCommand;
import org.apache.geode.management.cli.Result;
import org.apache.geode.management.internal.cli.AbstractCliAroundInterceptor;
import org.apache.geode.management.internal.cli.GfshParseResult;
import org.apache.geode.management.internal.cli.i18n.CliStrings;
import org.apache.geode.management.internal.cli.result.model.DataResultModel;
import org.apache.geode.management.internal.cli.result.model.FileResultModel;
import org.apache.geode.management.internal.cli.result.model.InfoResultModel;
import org.apache.geode.management.internal.cli.result.model.ResultModel;
import org.apache.geode.management.internal.configuration.domain.Configuration;
import org.apache.geode.management.internal.configuration.utils.ZipUtils;
import org.apache.geode.management.internal.security.ResourceOperation;
import org.apache.geode.security.ResourcePermission.Operation;
import org.apache.geode.security.ResourcePermission.Resource;
/**
* Commands for the cluster configuration
*/
@SuppressWarnings("unused")
public class ExportClusterConfigurationCommand extends GfshCommand {
private static final Logger logger = LogService.getLogger();
public static final String XML_FILE = "xml-file";
@CliCommand(value = {CliStrings.EXPORT_SHARED_CONFIG},
help = CliStrings.EXPORT_SHARED_CONFIG__HELP)
@CliMetaData(
interceptor = "org.apache.geode.management.internal.cli.commands.ExportClusterConfigurationCommand$ExportInterceptor",
relatedTopic = {CliStrings.TOPIC_GEODE_CONFIG})
@ResourceOperation(resource = Resource.CLUSTER, operation = Operation.READ)
public ResultModel exportSharedConfig(
@CliOption(key = GROUP,
specifiedDefaultValue = ConfigurationPersistenceService.CLUSTER_CONFIG,
unspecifiedDefaultValue = ConfigurationPersistenceService.CLUSTER_CONFIG) String group,
@CliOption(key = XML_FILE) String xmlFile,
@CliOption(key = CliStrings.EXPORT_SHARED_CONFIG__FILE,
help = CliStrings.EXPORT_SHARED_CONFIG__FILE__HELP) String zipFileName)
throws IOException {
if (!isSharedConfigurationRunning()) {
return ResultModel.createError("Cluster configuration service is not running.");
}
ResultModel result = new ResultModel();
InternalConfigurationPersistenceService configPersistenceService =
(InternalConfigurationPersistenceService) getConfigurationPersistenceService();
if (zipFileName != null) {
Path tempDir = Files.createTempDirectory("temp");
Path exportedDir = tempDir.resolve("cluster_config");
Path zipFile = tempDir.resolve(FilenameUtils.getName(zipFileName));
try {
for (Configuration config : configPersistenceService.getEntireConfiguration().values()) {
configPersistenceService.writeConfigToFile(config, exportedDir.toFile());
}
ZipUtils.zipDirectory(exportedDir, zipFile);
result.addFile(zipFile.toFile(), FileResultModel.FILE_TYPE_BINARY);
} catch (Exception e) {
logger.error("unable to export configuration.", e);
} finally {
FileUtils.deleteQuietly(tempDir.toFile());
}
} else {
Configuration configuration = configPersistenceService.getConfiguration(group);
if (configuration == null) {
return ResultModel.createError("No cluster configuration for '" + group + "'.");
}
String cacheXmlContent = configuration.getCacheXmlContent();
if (cacheXmlContent != null) {
InfoResultModel xmlSection = result.addInfo("xml");
xmlSection.setHeader(configuration.getCacheXmlFileName() + ": ");
xmlSection.addLine(cacheXmlContent);
}
Properties gemfireProperties = configuration.getGemfireProperties();
if (gemfireProperties.size() > 0) {
DataResultModel propertySection = result.addData("properties");
propertySection.setHeader("Properties: ");
propertySection.addData(gemfireProperties);
}
Set<String> jarNames = configuration.getJarNames();
if (jarNames.size() > 0) {
InfoResultModel jarSection = result.addInfo("jars");
jarSection.setHeader("Jars: ");
jarSection.addLine(Strings.join(jarNames, ", "));
}
}
return result;
}
/**
* Interceptor used by gfsh to intercept execution of export shared config command at "shell".
*/
public static class ExportInterceptor extends AbstractCliAroundInterceptor {
private String saveDirString;
private static final Logger logger = LogService.getLogger();
@Override
public ResultModel preExecution(GfshParseResult parseResult) {
String zip = parseResult.getParamValueAsString(CliStrings.EXPORT_SHARED_CONFIG__FILE);
String xmlFile = parseResult.getParamValueAsString(XML_FILE);
String group = parseResult.getParamValueAsString(GROUP);
if (group != null && group.contains(",")) {
return ResultModel.createError("Only a single group name is supported.");
}
if (zip != null && xmlFile != null) {
return ResultModel.createError("Zip file and xml File can't both be specified.");
}
if (zip != null && !group.equals(ConfigurationPersistenceService.CLUSTER_CONFIG)) {
return ResultModel.createError("zip file can not be exported with a specific group.");
}
if (zip != null && !zip.endsWith(".zip")) {
return ResultModel
.createError(CliStrings.format(CliStrings.INVALID_FILE_EXTENSION, ".zip"));
}
String exportedFile = (zip != null) ? zip : xmlFile;
if (exportedFile != null) {
// make sure the file does not exist so that we don't overwrite some existing file
File file = new File(exportedFile).getAbsoluteFile();
if (file.exists()) {
String message = file.getAbsolutePath() + " already exists. Overwrite it? ";
if (readYesNo(message, Response.YES) == Response.NO) {
return ResultModel.createError("Aborted. " + exportedFile + "already exists.");
}
}
}
return ResultModel.createInfo("");
}
@Override
public ResultModel postExecution(GfshParseResult parseResult, ResultModel result, Path tempFile)
throws IOException {
if (result.getStatus() == Result.Status.ERROR) {
return result;
}
String xmlFile = parseResult.getParamValueAsString(XML_FILE);
String zipFile = parseResult.getParamValueAsString(CliStrings.EXPORT_SHARED_CONFIG__FILE);
String group = parseResult.getParamValueAsString(GROUP);
// save the result to the file
if (xmlFile != null) {
InfoResultModel xmlSection = result.getInfoSection("xml");
if (xmlSection == null) {
InfoResultModel info = result.addInfo("info");
info.addLine(String.format("xml content is empty. %s is not created.", xmlFile));
} else {
File file = new File(xmlFile).getAbsoluteFile();
FileUtils.write(file, Strings.join(xmlSection.getContent(), System.lineSeparator()),
Charset.defaultCharset());
xmlSection.removeLine(0);
xmlSection.addLine("xml content exported to " + file.getAbsolutePath());
}
} else if (zipFile != null) {
// delete the existing file since at this point, user is OK to replace the old zip.
File file = new File(zipFile).getAbsoluteFile();
if (file.exists()) {
FileUtils.deleteQuietly(file);
}
result.saveFileTo(file.getParentFile());
}
return result;
}
}
}