/*
*
* RHQ Management Platform
* Copyright (C) 2005-2014 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation, and/or the GNU Lesser
* General Public License, version 2.1, also as published by the Free
* Software Foundation.
*
* 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 General Public License and the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.rhq.server.control.command;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Future;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.rhq.core.util.PropertiesFileUpdate;
import org.rhq.core.util.exception.ThrowableUtil;
import org.rhq.core.util.file.FileReverter;
import org.rhq.core.util.file.FileUtil;
import org.rhq.core.util.obfuscation.ObfuscatedPreferences.RestrictedFormat;
import org.rhq.core.util.obfuscation.PicketBoxObfuscator;
import org.rhq.core.util.stream.StreamUtil;
import org.rhq.server.control.ControlCommand;
import org.rhq.server.control.RHQControl;
import org.rhq.server.control.RHQControlException;
import org.rhq.server.control.util.ExecutorAssist;
/**
* @author Jay Shaughnessy
* @author John Mazzitelli
* @author Heiko W. Rupp
*/
public class Upgrade extends AbstractInstall {
private static final String FROM_SERVER_DIR_OPTION = "from-server-dir";
private static final String USE_REMOTE_STORAGE_NODE = "use-remote-storage-node";
private static final String STORAGE_DATA_ROOT_DIR = "storage-data-root-dir";
private static final String LIST_VERSIONS_OPTION = "list-versions";
private static final String STORAGE_SCHEMA_OPTION = "storage-schema";
private static final String APPLY_FIXES = "apply-fixes";
private Options options;
public Upgrade() {
options = new Options()
.addOption(
null,
FROM_AGENT_DIR_OPTION,
true,
"Full path to install directory of the RHQ Agent to be upgraded. Required only if an existing agent "
+ "exists and is not installed in the default location: <server-dir>/../rhq-agent")
.addOption(null, FROM_SERVER_DIR_OPTION, true,
"Full path to install directory of the RHQ Server to be upgraded. Required.")
.addOption(null, START_OPTION, false,
"Deprecated. This option has no effect and will be removed in a future version.")
.addOption(
null,
USE_REMOTE_STORAGE_NODE,
true,
"By default a server is co-located with a storage node. However, if this option is set to true, no local "
+ "storage node will be upgraded and it is assumed a remote storage node is configured in rhq-server.properties.")
.addOption(
null,
LIST_VERSIONS_OPTION,
false,
"This option prints the install versions for the Servers and Storage Nodes in the topology. This option must be run "
+ "from an already installed or upgraded RHQ Server node. When specified, all other options are ignored. ")
.addOption(
null,
STORAGE_DATA_ROOT_DIR,
true,
"This option is valid only when upgrading from older systems that did not have storage nodes. Use this option to specify a non-default base "
+ "directory for the data directories created by the storage node. For example, if the default directory is "
+ "not writable for the current user (/var/lib on Linux) or if you simply prefer a different location. ")
.addOption(
null,
APPLY_FIXES,
false,
"This option applies manual fixes to the storage or RHQ server. Ran automatically by the utilities if needed."
)
.addOption(
null,
STORAGE_SCHEMA_OPTION,
false,
"This option updates the storage cluster schema. This option must be run from an RHQ Server node. The running time for "
+ "the schema update will vary depending on the schema changes being made, the runtime may be long and should not be "
+ "interrupted. It requires that all servers and storage nodes have already been upgraded to the desired version "
+ "and that all nodes in the storage cluster are running. The command will fail if these prerequisites are not met. "
+ "Use 'rhqctl upgrade --list-versions' to see the current upgrade status. When specified, all other options "
+ "are ignored. Not all upgrades will require this step.");
}
@Override
public String getName() {
return "upgrade";
}
@Override
public String getDescription() {
return "Upgrades RHQ services from an earlier installed version";
}
@Override
public Options getOptions() {
return options;
}
@Override
protected String getReadmeFilename() {
return "UPGRADE_README.txt";
}
@Override
protected int exec(CommandLine commandLine) {
if (commandLine.hasOption(LIST_VERSIONS_OPTION)) {
return listVersions(commandLine);
}
if (commandLine.hasOption(STORAGE_SCHEMA_OPTION)) {
return updateStorageSchema(commandLine);
}
if (commandLine.hasOption(APPLY_FIXES)) {
return applyFixes(commandLine);
}
int rValue = RHQControl.EXIT_CODE_OK;
List<String> errors = null;
try {
errors = validateOptions(commandLine);
if (!errors.isEmpty()) {
for (String error : errors) {
log.error(error);
}
log.error("Exiting due to the previous errors");
return RHQControl.EXIT_CODE_OPERATION_FAILED;
}
// If storage or server appear to be installed already then don't perform an upgrade. It's OK
// if the agent already exists in the default location, it may be there from a prior install.
if (isStorageInstalled() || isServerInstalled()) {
log.warn("RHQ is already installed so upgrade can not be performed.");
return RHQControl.EXIT_CODE_OPERATION_FAILED;
}
// Attempt to shutdown any running components. A failure to shutdown a component is not a failure as it
// really shouldn't be running anyway. This is just an attempt to avoid upgrade problems.
log.info("Stopping any running RHQ components...");
// Stop the agent, if running.
if (commandLine.hasOption(FROM_AGENT_DIR_OPTION)) {
rValue = Math.max(rValue, killAgent(getFromAgentDir(commandLine))); // this validates the path as well
}
// If rhqctl exists in the old version, use it to stop old components, otherwise, just try and stop the
// server using the legacy script. If there is no rhqctl, there is no storage node anyway, so we just
// stop server in that case.
File fromBinDir = new File(getFromServerDir(commandLine), "bin");
org.apache.commons.exec.CommandLine rhqctlStop = isRhq48OrLater(commandLine) ? getCommandLine(false,
"rhqctl", "stop") : getCommandLine("rhq-server", "stop");
int exitValue = ExecutorAssist.execute(fromBinDir, rhqctlStop);
if (exitValue == 0) {
log.info("The old installation components have been stopped");
} else {
log.error("The old installation components failed to be stopped. Please stop them manually before continuing. exit code="
+ exitValue);
return exitValue;
}
// If any failures occur during upgrade, we know we need to reset rhq-server.properties
final FileReverter serverPropFileReverter = new FileReverter(getServerPropertiesFile());
addUndoTask(new ControlCommand.UndoTask("Reverting server properties file ["
+ getServerPropertiesFile().getName() + "]") {
public void performUndoWork() throws Exception {
try {
serverPropFileReverter.revert();
} catch (Exception e) {
throw new Exception("Cannot reset rhq-server.properties - revert settings manually", e);
}
}
});
// now upgrade everything
upgradeServerEnvFile(commandLine);
rValue = Math.max(rValue, upgradeStorage(commandLine));
rValue = Math.max(rValue, upgradeServer(commandLine));
rValue = Math.max(rValue, upgradeAgent(commandLine));
File agentDir;
if (commandLine.hasOption(FROM_AGENT_DIR_OPTION)) {
agentDir = new File(commandLine.getOptionValue(FROM_AGENT_DIR_OPTION));
} else {
agentDir = getAgentBasedir();
}
updateWindowsAgentService(agentDir);
} catch (Exception e) {
throw new RHQControlException("An error occurred while executing the upgrade command", e);
} finally {
if (errors == null || errors.isEmpty()) {
try {
if (isServerInstalled()) {
log.info("\n\n========== UPGRADE SUMMARY ==========");
listVersions(commandLine);
}
Stop stopCommand = new Stop();
stopCommand.exec(new String[] { "--server" });
rValue = Math.max(rValue, stopCommand.exec(new String[] { "--storage" }));
} catch (Throwable t) {
log.warn("Unable to stop services: " + t.getMessage());
rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
}
}
return rValue;
}
private int upgradeStorage(CommandLine rhqctlCommandLine) throws Exception {
if (rhqctlCommandLine.hasOption(USE_REMOTE_STORAGE_NODE)) {
log.info("Ignoring storage node upgrade, a remote storage node is configured.");
return RHQControl.EXIT_CODE_OK;
}
int rValue = RHQControl.EXIT_CODE_OK;
// If upgrading from a pre-cassandra then just install an initial storage node. Otherwise, upgrade
if (isRhq48OrLater(rhqctlCommandLine)) {
try {
final String fromServerPath = getFromServerDir(rhqctlCommandLine).getAbsolutePath();
// We need to be sure the storage is really stopped (long enough) to not get a port conflict
waitForProcessToStop(getStoragePid());
// if the upgrade fails, we need to purge the new storage node basedir to allow for user to try again
// later
addUndoTask(new ControlCommand.UndoTask("Removing new storage node install directory") {
public void performUndoWork() {
try {
org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-storage-installer",
"--undo", fromServerPath);
ExecutorAssist.execute(getBinDir(), commandLine);
} catch (Exception e) {
log.warn("Unable to undo version stamp for storage node: " + e.getMessage());
}
FileUtil.purge(getStorageBasedir(), true);
}
});
addUndoTaskToStopComponent("--storage"); // The undo tasks are done in reversed order
org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-storage-installer", "--upgrade",
fromServerPath);
rValue = ExecutorAssist.execute(getBinDir(), commandLine);
log.info("The storage node upgrade has finished with an exit value of [" + rValue + "]");
} catch (IOException e) {
log.error("An error occurred while running the storage node upgrade: " + e.getMessage());
throw e;
}
} else {
rValue = installStorageNode(getStorageBasedir(), rhqctlCommandLine, true);
}
// Only an exception will stop the upgrade progress and invoke the undo logic.
if (rValue != RHQControl.EXIT_CODE_OK) {
throw new RuntimeException("The storage node upgrade failed with exit code [" + rValue + "]");
}
return RHQControl.EXIT_CODE_OK;
}
private int upgradeServer(CommandLine commandLine) throws Exception {
// don't upgrade the server if this is a storage node only install
File oldServerDir = getFromServerDir(commandLine);
if (!(!isRhq48OrLater(commandLine) || isServerInstalled(oldServerDir))) {
log.info("Ignoring server upgrade, the '--from-server-dir' is a storage node only installation.");
return RHQControl.EXIT_CODE_OK;
}
// copy all the old settings into the new rhq-server.properties file
upgradeServerPropertiesFile(commandLine);
int rValue = RHQControl.EXIT_CODE_OK;
// make sure we retain the oracle driver if one exists
try {
copyOracleDriver(oldServerDir);
} catch (Exception e) {
log.error("Failed to copy the old Oracle driver to the new server. "
+ "The upgrade will continue but your server may not work if connecting to an Oracle database, "
+ "in which case you will need to manually install an Oracle driver to your server. " + "Cause: "
+ ThrowableUtil.getAllMessages(e));
rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
// copy over any wrapper.inc that may have been added
File oldWrapperIncFile = new File(oldServerDir, "bin/wrapper/rhq-server-wrapper.inc");
if (oldWrapperIncFile.exists()) {
File newWrapperIncFile = new File(getBaseDir(), "bin/wrapper/rhq-server-wrapper.inc");
FileUtil.copyFile(oldWrapperIncFile, newWrapperIncFile);
}
// start the server, then invoke the installer and wait for the server to be completely installed
rValue = Math.max(rValue, startRHQServerForInstallation());
Future<Integer> integerFuture = runRHQServerInstaller(ServerInstallerAction.UPGRADE);
waitForRHQServerToInitialize(integerFuture);
rValue = Math.max(rValue, integerFuture.get());
return rValue;
}
public void copyOracleDriver(File oldServerDir) throws IOException {
// RHQ doesn't ship the Oracle driver. If the user uses Oracle, they have their own driver so we need to copy it over.
// Because the module.xml has the driver name in it, we need to copy the full Oracle JDBC driver module content.
// Look in the new server install and see if we do not have a real oracle JDBC driver.
// If the new server only has our "dummy" driver, we copy over the old driver module to the new server.
// If the new server already has a "real" driver, leave anything else in place as it may be a newer driver.
String oracleModuleRelativePath = "modules/org/rhq/oracle";
File newOracleModuleDir = new File(getBaseDir(), oracleModuleRelativePath);
File newOracleModuleMainDir = new File(newOracleModuleDir, "main");
// first see if the new server was already given a real oracle driver - if so, there is nothing for us to do
for (File f : newOracleModuleMainDir.listFiles()) {
boolean foundRealOracleDriver = f.isFile() && f.length() > 100000L; // the actual driver is much bigger, our fake one is small
if (foundRealOracleDriver == true) {
log.info("Looks like the new server already has an Oracle driver: " + f);
return; // nothing for us to do since the new server already appears to have a real oracle driver
}
}
// now see if we are updating a newer JBossAS7+ based server - if so, the old oracle driver is in a module
File oldOracleModuleDir = new File(oldServerDir, oracleModuleRelativePath);
if (oldOracleModuleDir.isDirectory()) {
FileUtil.purge(newOracleModuleDir, true); // clean out anything that might be in here
FileUtil.copyDirectory(oldOracleModuleDir, newOracleModuleDir);
log.info("Copied the old Oracle JDBC module [" + oldOracleModuleDir + "] to the new server: "
+ newOracleModuleDir);
} else {
// we aren't updating a newer JBossAS7+ based server, its probably an older JBossAS 4.2.3 based server
// where the oracle jar is located in a different directory (jbossas/server/default/lib/ojdbc*.jar)
File oldLibDir = new File(oldServerDir, "jbossas/server/default/lib");
if (oldLibDir.isDirectory()) {
FilenameFilter oracleDriverFilenameFilter = new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.startsWith("ojdbc") && name.endsWith(".jar");
}
};
// find the old ojdbc driver
File[] oracleDriver = oldLibDir.listFiles(oracleDriverFilenameFilter);
if (oracleDriver != null && oracleDriver.length > 0) {
if (oracleDriver.length > 1) {
log.warn("It appears that more than one oracle driver exists in the old server at ["
+ oldLibDir + "]; this one will be reused: " + oracleDriver[0]);
}
// we need to remove the dummy oracle driver file from the new server first
File[] dummy = newOracleModuleMainDir.listFiles(oracleDriverFilenameFilter);
if (dummy != null) {
for (File dummyFileToDelete : dummy) { // there should only be one, but just remove all ojdbc*.jar files
dummyFileToDelete.delete();
}
}
// copy the real oracle driver to our new server's oracle module
File newOracleJarFile = new File(newOracleModuleMainDir, oracleDriver[0].getName());
FileUtil.copyFile(oracleDriver[0], newOracleJarFile);
log.info("Copied the old Oracle JDBC driver [" + oracleDriver[0] + "] to the new server: "
+ newOracleJarFile);
// now we need to update the module.xml file so it points to the new oracle driver
File moduleXmlFile = new File(newOracleModuleMainDir, "module.xml");
String originalXml = new String(StreamUtil.slurp(new FileInputStream(moduleXmlFile)));
String newXml = originalXml.replaceFirst("resource-root path.*=.*\"ojdbc.*jar\"",
"resource-root path=\"" + oracleDriver[0].getName() + "\"");
FileUtil.writeFile(new ByteArrayInputStream(newXml.getBytes()), moduleXmlFile);
log.info("Updated module.xml [" + moduleXmlFile + "] to use the proper Oracle driver");
}
}
}
return;
}
/**
* If there is an rhq-server-env.sh|bat file in the old server directory then:<pre>
* 1) backup the new version as rhq.server-env.sh|bat.default.
* 2) copy the old version to the new server so it can be applied to the upgrade.
* </pre>
* @param commandLine
* @throws Exception
*/
private void upgradeServerEnvFile(CommandLine commandLine) throws Exception {
File oldServerDir = getFromServerDir(commandLine);
String[] envFiles = new String[] { "bin/rhq-server-env.sh", "bin/rhq-server-env.bat" };
for (String envFile : envFiles) {
File oldServerEnvFile = new File(oldServerDir, envFile);
if (oldServerEnvFile.exists()) {
File newServerEnvFile = new File(getBaseDir(), envFile);
File newServerEnvFileBackup = new File(getBaseDir(), (envFile + ".default"));
try {
// If any failures occur during upgrade reset the env file to the default
final FileReverter serverEnvFileReverter = new FileReverter(newServerEnvFile);
addUndoTask(new ControlCommand.UndoTask("Reverting server environment file ["
+ newServerEnvFile.getName() + "]") {
public void performUndoWork() throws Exception {
try {
serverEnvFileReverter.revert();
} catch (Exception e) {
throw new Exception("Cannot reset rhq-server-env.sh|bat - revert manually", e);
}
}
});
FileUtil.copyFile(newServerEnvFile, newServerEnvFileBackup);
newServerEnvFile.delete();
FileUtil.copyFile(oldServerEnvFile, newServerEnvFile);
} catch (Exception e) {
// log a message about this problem, but we will let the upgrade continue
log.error("Failed to update [" + oldServerEnvFile + "] to [" + newServerEnvFile
+ "]. Settings in + [" + oldServerEnvFile + "] + will not be applied to the upgrade. "
+ "You will need to manually copy the file to the new location after the upgrade.");
}
}
}
return;
}
private void upgradeServerPropertiesFile(CommandLine commandLine) throws Exception {
File oldServerDir = getFromServerDir(commandLine);
File oldServerPropsFile = new File(oldServerDir, "bin/rhq-server.properties");
Properties oldServerProps = new Properties();
FileInputStream oldServerPropsFileInputStream = new FileInputStream(oldServerPropsFile);
try {
oldServerProps.load(oldServerPropsFileInputStream);
} finally {
oldServerPropsFileInputStream.close();
}
oldServerProps.setProperty("rhq.autoinstall.enabled", "true"); // ensure that we always enable the installer
oldServerProps.setProperty("rhq.autoinstall.database", "auto"); // the old value could have been "overwrite" - NOT what we want when upgrading
// For upgrades the RHQ Server super-user should already exist. But to pass server properties file validation
// this property must be set. Setting it to an invalid plain-text value will fail the upgrade if for some reason
// the rhqadmin user does not exist, which is, I think, what we would want to have happen.
oldServerProps.setProperty("rhq.autoinstall.server.admin.password", "ignored-on-upgrade");
// remove some old, obsolete settings no longer needed or used
oldServerProps.remove("rhq.server.embedded-agent.name");
oldServerProps.remove("rhq.server.embedded-agent.reset-configuration");
oldServerProps.remove("rhq.server.embedded-agent.disable-native-system");
oldServerProps.remove("rhq.server.embedded-agent.enabled");
oldServerProps.remove("rhq.server.startup.jrmpinvoker.rmiport");
oldServerProps.remove("rhq.server.startup.webservice.port");
oldServerProps.remove("rhq.server.startup.unifiedinvoker.port");
oldServerProps.remove("rhq.server.startup.namingservice.rmiport");
oldServerProps.remove("rhq.server.startup.pooledinvoker.rmiport");
oldServerProps.remove("rhq.server.startup.ajp.port");
oldServerProps.remove("rhq.server.startup.namingservice.port");
oldServerProps.remove("rhq.server.startup.aspectdeployer.bind-port");
oldServerProps.remove("rhq.server.plugin-deployer-threads");
oldServerProps.remove("rhq.server.database.xa-datasource-class");
oldServerProps.remove("rhq.server.database.driver-class");
oldServerProps.remove("java.rmi.server.hostname");
// do not set the keystore/truststore algorithms if they are the defaults to allow for runtime defaults to take effect
String[] algPropNames = new String[] { "rhq.communications.connector.security.truststore.algorithm", //
"rhq.communications.connector.security.keystore.algorithm", //
"rhq.server.client.security.keystore.algorithm", //
"rhq.server.client.security.truststore.algorithm", //
"rhq.server.tomcat.security.algorithm" };
for (String algPropName : algPropNames) {
String algValue = oldServerProps.getProperty(algPropName, "SunX509");
if (algValue.equals("SunX509") || algValue.equals("IbmX509")) {
oldServerProps.remove(algPropName); // let the default take effect at runtime - which will depend on the JVM
}
}
// the older servers stored the HTTP and HTTPS ports under different names - make sure we reuse those ports with the new properties
String httpPort = oldServerProps.getProperty("rhq.server.startup.web.http.port");
if (httpPort != null) {
oldServerProps.remove("rhq.server.startup.web.http.port");
oldServerProps.setProperty("rhq.server.socket.binding.port.http", httpPort);
}
String httpsPort = oldServerProps.getProperty("rhq.server.startup.web.https.port");
if (httpsPort != null) {
oldServerProps.remove("rhq.server.startup.web.https.port");
oldServerProps.setProperty("rhq.server.socket.binding.port.https", httpsPort);
}
//Migrate storage node properties
String storageUsername = oldServerProps.getProperty("rhq.cassandra.username");
if (storageUsername != null) {
oldServerProps.remove("rhq.cassandra.username");
oldServerProps.setProperty("rhq.storage.username", storageUsername);
}
String storagePassword = oldServerProps.getProperty("rhq.cassandra.password");
if (storagePassword != null) {
// In RHQ 4.8 the Cassandra username/password had to be rhqadmin/rhqadmin; so,
// we can safely set rhq.storage.password to the obfuscated version freeing the
// user of performing the additional step of generated the obfuscated password.
oldServerProps.remove("rhq.cassandra.password");
oldServerProps.setProperty("rhq.storage.password", "1eeb2f255e832171df8592078de921bc");
}
String storageSeeds = oldServerProps.getProperty("rhq.cassandra.seeds");
if (storageSeeds != null) {
StringBuffer storageNodes = new StringBuffer();
String cqlPort = "";
String[] unparsedNodes = storageSeeds.split(",");
for (int index = 0; index < unparsedNodes.length; index++) {
String[] params = unparsedNodes[index].split("\\|");
if (params.length == 3) {
storageNodes.append(params[0]);
if (index < unparsedNodes.length - 1) {
storageNodes.append(",");
}
cqlPort = params[2];
}
}
oldServerProps.remove("rhq.cassandra.seeds");
oldServerProps.setProperty("rhq.storage.nodes", storageNodes.toString());
oldServerProps.setProperty("rhq.storage.cql-port", cqlPort);
}
String storageCompression = oldServerProps.getProperty("rhq.cassandra.client.compression-enabled");
if (storageCompression != null) {
oldServerProps.remove("rhq.cassandra.client.compression-enabled");
oldServerProps.setProperty("rhq.storage.client.compression-enabled", storageCompression);
}
// copy the old key/truststore files from the old location to the new server configuration directory
copyReferredFile(commandLine, oldServerProps, "rhq.server.tomcat.security.keystore.file");
copyReferredFile(commandLine, oldServerProps, "rhq.server.tomcat.security.truststore.file");
copyReferredFile(commandLine, oldServerProps, "rhq.communications.connector.security.keystore.file");
copyReferredFile(commandLine, oldServerProps, "rhq.communications.connector.security.truststore.file");
copyReferredFile(commandLine, oldServerProps, "rhq.server.client.security.keystore.file");
copyReferredFile(commandLine, oldServerProps, "rhq.server.client.security.truststore.file");
// for oracle, ensure the unused properties are set to unused, otherwise prop file validation may fail
String dbType = oldServerProps.getProperty("rhq.server.database.type-mapping");
if (null != dbType && dbType.toLowerCase().contains("oracle")) {
oldServerProps.setProperty("rhq.server.database.server-name", "unused");
oldServerProps.setProperty("rhq.server.database.port", "unused");
oldServerProps.setProperty("rhq.server.database.db-name", "unused");
}
migrateRestrictedProperties(oldServerProps);
// now merge the old settings in with the default properties from the new server install
String newServerPropsFilePath = getServerPropertiesFile().getAbsolutePath();
PropertiesFileUpdate newServerPropsFile = new PropertiesFileUpdate(newServerPropsFilePath);
newServerPropsFile.update(oldServerProps);
return;
}
/**
* Given server properties and a property name whose value is a file path, see if we need to copy the referred
* file to the new server location. This will set the new property value in the given properties if it needed to be
* updated.
*
* @param commandLine the rhqctl command line
* @param properties the properties
* @param propertyName name of a property whose value refers to a file path
*/
private void copyReferredFile(CommandLine commandLine, Properties properties, String propertyName) {
String propertyValue = properties.getProperty(propertyName);
if (propertyValue == null || propertyValue.trim().length() == 0) {
return;
}
File referredFile = new File(propertyValue);
boolean originalFilePathIsAbsolute = referredFile.isAbsolute();
if (!originalFilePathIsAbsolute) {
// If its not absolute, we assume it is using some default syntax used in earlier versions
// and we will know this if the value starts with one of the following:
// ${jboss.server.config.dir}
// ${jboss.server.home.dir}/conf
// conf/
// All of which refer to the old server's configuration directory.
File oldServerConfigDir = new File(getFromServerDir(commandLine), "jbossas/standalone/configuration");
if (!oldServerConfigDir.isDirectory()) {
// the older RHQ releases had the old JBossAS 4.2.3 directory structure
oldServerConfigDir = new File(getFromServerDir(commandLine), "jbossas/server/default/conf");
if (!oldServerConfigDir.isDirectory()) {
log.warn("Cannot determine the old server's configuration directory - cannot copy over the old file: "
+ referredFile);
return;
}
}
String absPath = propertyValue.replace("${jboss.server.config.dir}", oldServerConfigDir.getAbsolutePath());
absPath = absPath.replace("${jboss.server.home.dir}/conf",
useForwardSlash(oldServerConfigDir.getAbsolutePath()));
if (absPath.startsWith("conf/")) {
absPath = absPath.replaceFirst("conf", useForwardSlash(oldServerConfigDir.getAbsolutePath()));
}
referredFile = new File(absPath);
}
if (!referredFile.isFile()) {
log.info("Server property [" + propertyName + "] refers to file [" + referredFile
+ "] that does not exist. Skipping.");
return;
}
File newServerConfigDir = new File(getBaseDir(), "jbossas/standalone/configuration");
File newFile = new File(newServerConfigDir, referredFile.getName());
try {
FileUtil.copyFile(referredFile, newFile);
} catch (Exception e) {
// log a message about this problem, but we will let the upgrade continue
log.error("Failed to copy the old file [" + referredFile + "] referred to by server property ["
+ propertyName + "] to the new location of [" + newFile
+ "]. You will need to manually copy that file to the new location."
+ "The server may not work properly until you do this.");
}
properties.setProperty(propertyName, "${jboss.server.config.dir}/" + newFile.getName());
return;
}
private String useForwardSlash(String path) {
return (null != path) ? path.replace('\\', '/') : null;
}
private int upgradeAgent(CommandLine rhqctlCommandLine) throws Exception {
try {
File oldAgentDir;
if (rhqctlCommandLine.hasOption(FROM_AGENT_DIR_OPTION)) {
oldAgentDir = new File(rhqctlCommandLine.getOptionValue(FROM_AGENT_DIR_OPTION));
if (!oldAgentDir.isDirectory()) {
throw new FileNotFoundException("Missing agent to upgrade: " + oldAgentDir.getAbsolutePath());
}
} else {
oldAgentDir = null;
File fromServerDir = getFromServerDir(rhqctlCommandLine);
if (fromServerDir != null && fromServerDir.isDirectory()) {
File fromServerDirParent = fromServerDir.getParentFile();
if (fromServerDirParent != null && fromServerDirParent.isDirectory()) {
oldAgentDir = new File(fromServerDirParent, "rhq-agent");
}
}
if (!oldAgentDir.isDirectory()) {
log.info("No " + FROM_AGENT_DIR_OPTION
+ " option specified and no agent found in the default location ["
+ oldAgentDir.getAbsolutePath()
+ "]. Installing agent in the default location as part of the upgrade.");
return installAgent(getAgentBasedir(), rhqctlCommandLine);
}
}
log.info("Upgrading RHQ agent located at: " + oldAgentDir.getAbsolutePath());
final File agentBasedir = getAgentBasedir();
File agentInstallerJar = getFileDownload("rhq-agent", "rhq-enterprise-agent");
int exitValue = updateAndMoveExistingAgent(agentBasedir, oldAgentDir, agentInstallerJar);
addUndoTask(new ControlCommand.UndoTask("Removing agent install directory") {
public void performUndoWork() {
FileUtil.purge(agentBasedir, true);
}
});
log.info("The agent has been upgraded and placed in: " + agentBasedir);
return exitValue;
} catch (IOException e) {
log.error("An error occurred while upgrading the agent: " + e.getMessage());
throw e;
}
}
/**
* Migrates default restricted properties to the correct restricted/obfuscated format.
*
* @param properties properties to migrate
*/
private void migrateRestrictedProperties(Properties properties) {
List<String> defaultRestrictedProperties = new ArrayList<String>();
defaultRestrictedProperties.add("rhq.server.client.security.keystore.password");
defaultRestrictedProperties.add("rhq.server.client.security.keystore.key-password");
defaultRestrictedProperties.add("rhq.server.client.security.truststore.password");
defaultRestrictedProperties.add("rhq.communications.connector.security.keystore.key-password");
defaultRestrictedProperties.add("rhq.communications.connector.security.keystore.password");
defaultRestrictedProperties.add("rhq.communications.connector.security.truststore.password");
defaultRestrictedProperties.add("rhq.server.tomcat.security.keystore.password");
defaultRestrictedProperties.add("rhq.server.tomcat.security.keystore.key-password");
defaultRestrictedProperties.add("rhq.server.tomcat.security.truststore.password");
for (String restrictedProperty : defaultRestrictedProperties) {
String value = (String) properties.get(restrictedProperty);
if (value == null) {
continue;
}
try {
if (RestrictedFormat.isRestrictedFormat(value)) {
value = RestrictedFormat.retrieveValue(value);
PicketBoxObfuscator.decode(value);
} else {
throw new Exception("Value not in a restricted format");
}
} catch (Exception ex) {
properties.put(restrictedProperty, RestrictedFormat.formatValue(PicketBoxObfuscator.encode(value)));
}
}
}
private List<String> validateOptions(CommandLine commandLine) {
List<String> errors = new LinkedList<String>();
// When updating storage cluster schema everything else is ignored
if (commandLine.hasOption(STORAGE_SCHEMA_OPTION) || commandLine.hasOption(LIST_VERSIONS_OPTION)) {
return errors;
}
if (!commandLine.hasOption(FROM_SERVER_DIR_OPTION)) {
errors.add("Missing required option: " + FROM_SERVER_DIR_OPTION);
} else {
File fromServerDir = new File(commandLine.getOptionValue(FROM_SERVER_DIR_OPTION));
if (!fromServerDir.isDirectory()) {
errors.add("The " + FROM_SERVER_DIR_OPTION + " directory does not exist: [" + fromServerDir.getPath()
+ "]");
} else {
File serverPropsFile = new File(fromServerDir, "bin/rhq-server.properties");
if (!serverPropsFile.isFile()) {
errors.add("The " + FROM_SERVER_DIR_OPTION
+ " directory does not appear to be an RHQ installation. Missing expected file: ["
+ serverPropsFile.getPath() + "]");
}
}
}
if (isRhq48OrLater(commandLine)) {
if (commandLine.hasOption(STORAGE_DATA_ROOT_DIR)) {
errors.add("The option --" + STORAGE_DATA_ROOT_DIR
+ " is valid only for upgrades from older systems that did not have storage nodes.");
}
}
if (commandLine.hasOption(FROM_AGENT_DIR_OPTION)) {
File fromAgentDir = new File(commandLine.getOptionValue(FROM_AGENT_DIR_OPTION));
if (!fromAgentDir.isDirectory()) {
errors.add("The " + FROM_AGENT_DIR_OPTION + " directory does not exist: [" + fromAgentDir.getPath()
+ "]");
} else {
String agentEnvFileName = (File.separatorChar == '/') ? "bin/rhq-agent-env.sh"
: "bin/rhq-agent-env.bat";
File agentEnvFile = new File(fromAgentDir, agentEnvFileName);
if (!agentEnvFile.isFile()) {
errors.add("The " + FROM_AGENT_DIR_OPTION
+ " directory does not appear to be an RHQ Agent installation. Missing expected file: ["
+ agentEnvFile.getPath() + "]");
}
}
}
return errors;
}
static public File getFromServerDir(CommandLine commandLine) {
return (commandLine.hasOption(FROM_SERVER_DIR_OPTION)) ? new File(
commandLine.getOptionValue(FROM_SERVER_DIR_OPTION)) : null;
}
protected boolean isRhq48OrLater(CommandLine commandLine) {
return new File(getFromServerDir(commandLine), "bin/rhqctl").exists();
}
protected boolean isRhq410OrLater(CommandLine commandLine) {
return new File(getFromServerDir(commandLine), "bin/internal").isDirectory();
}
private int updateStorageSchema(CommandLine commandLine) {
// Only performed from a server install
if (!isServerInstalled()) {
log.info("This command can only be performed from an installed Server node. This is either a standalone Storage Node, or the Server has yet to be installed.");
return RHQControl.EXIT_CODE_OPERATION_FAILED;
}
int rValue = RHQControl.EXIT_CODE_OK;
try {
Future<Integer> integerFuture = runRHQServerInstaller(ServerInstallerAction.UPDATESTORAGESCHEMA);
rValue = Math.max(rValue, integerFuture.get());
} catch (Exception e) {
log.error("Updating the RHQ Storage Cluster schema failed: " + e.getMessage());
rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
return rValue;
}
private int applyFixes(CommandLine commandLine) {
if (!isServerInstalled()) {
log.info("This command can only be performed from an installed Server node. This is either a standalone Storage Node, or the Server has yet to be installed.");
return RHQControl.EXIT_CODE_OPERATION_FAILED;
}
int rValue = RHQControl.EXIT_CODE_OK;
try {
Future<Integer> integerFuture = runRHQServerInstaller(ServerInstallerAction.CLEARCOLUMNFAMILIES);
rValue = Math.max(rValue, integerFuture.get());
} catch (Exception e) {
log.error("Fixing the RHQ Storage Cluster or Server failed: " + e.getMessage());
rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
return rValue;
}
private int listVersions(CommandLine commandLine) {
// Only performed from a server install
if (!isServerInstalled()) {
log.info("This command can only be performed from an installed Server node. This is either a standalone Storage Node, or the Server has yet to be installed.");
return RHQControl.EXIT_CODE_OPERATION_FAILED;
}
int rValue = RHQControl.EXIT_CODE_OK;
try {
Future<Integer> integerFuture = runRHQServerInstaller(ServerInstallerAction.LISTVERSIONS);
//waitForRHQServerToInitialize(integerFuture);
rValue = Math.max(rValue, integerFuture.get());
} catch (Exception e) {
log.error("Reporting topology status failed: " + e.getMessage());
rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
return rValue;
}
}