/*
*
* * RHQ Management Platform
* * Copyright (C) 2005-2013 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.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Future;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.jboss.as.controller.client.ModelControllerClient;
import org.rhq.common.jbossas.client.controller.DeploymentJBossASClient;
import org.rhq.common.jbossas.client.controller.MCCHelper;
import org.rhq.core.util.PropertiesFileUpdate;
import org.rhq.core.util.file.FileReverter;
import org.rhq.core.util.file.FileUtil;
import org.rhq.core.util.file.FileVisitor;
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.RHQPosixParser;
import org.rhq.server.control.util.ExecutorAssist;
/**
* Common code for commands that perform installs. Basically shared code for Install and Upgrade commands.
*
* @author Jay Shaughnessy
*/
public abstract class AbstractInstall extends ControlCommand {
protected static final String AGENT_CONFIG_OPTION = "agent-config";
protected static final String FROM_AGENT_DIR_OPTION = "from-agent-dir";
protected static final String START_OPTION = "start";
protected static final String AGENT_PREFERENCE = "agent-preference";
protected static final String STORAGE_DATA_ROOT_DIR = "storage-data-root-dir";
// some known agent preference setting names
private static final String PREF_RHQ_AGENT_CONFIGURATION_SETUP_FLAG = "rhq.agent.configuration-setup-flag";
private static final String PREF_RHQ_AGENT_AUTO_UPDATE_FLAG = "rhq.agent.agent-update.enabled";
private static final String PREF_RHQ_AGENT_SECURITY_TOKEN = "rhq.agent.security-token";
private static final String PREF_RHQ_AGENT_SERVER_TRANSPORT = "rhq.agent.server.transport";
private static final String PREF_RHQ_AGENT_SERVER_BINDADDRESS = "rhq.agent.server.bind-address";
private static final String PREF_RHQ_AGENT_SERVER_BINDPORT = "rhq.agent.server.bind-port";
private static final String PREF_RHQ_AGENT_SERVER_TRANSPORTPARAMS = "rhq.agent.server.transport-params";
// EAP related
private static final String PREF_JBOSS_MANAGEMENT_NATIVE_PORT = "jboss.management.native.port";
protected int installWindowsService(File workingDir, String batFile, boolean replaceExistingService, boolean start)
throws Exception {
org.apache.commons.exec.CommandLine commandLine;
int rValue = RHQControl.EXIT_CODE_OK;
if (replaceExistingService) {
commandLine = getCommandLine(batFile, "stop");
rValue = Math.max(rValue, ExecutorAssist.execute(workingDir, commandLine));
commandLine = getCommandLine(batFile, "remove");
rValue = Math.max(rValue, ExecutorAssist.execute(workingDir, commandLine));
}
commandLine = getCommandLine(batFile, "install");
rValue = Math.max(rValue, ExecutorAssist.execute(workingDir, commandLine));
if (start) {
commandLine = getCommandLine(batFile, "start");
rValue = Math.max(rValue, ExecutorAssist.execute(workingDir, commandLine));
}
return rValue;
}
protected void validateCustomStorageDataDirectories(CommandLine commandLine, List<String> errors) {
StorageDataDirectories customDataDirs = getCustomStorageDataDirectories(commandLine);
if (customDataDirs != null) {
if (customDataDirs.basedir.isAbsolute()) {
if (!isDirectoryEmpty(customDataDirs.dataDir)) {
errors.add("Storage data directory [" + customDataDirs.dataDir + "] is not empty.");
}
if (!isDirectoryEmpty(customDataDirs.commitlogDir)) {
errors.add("Storage commitlog directory [" + customDataDirs.commitlogDir + "] is not empty.");
}
if (!isDirectoryEmpty(customDataDirs.savedcachesDir)) {
errors.add("Storage saved-caches directory [" + customDataDirs.savedcachesDir + "] is not empty.");
}
} else {
errors.add("The storage root directory [" + customDataDirs.basedir
+ "] must be specified with an absolute path and should be outside of the main install directory.");
}
}
}
private boolean isDirectoryEmpty(File dir) {
if (dir.isDirectory()) {
File[] files = dir.listFiles();
return (files == null || files.length == 0);
} else {
return true;
}
}
protected void waitForRHQServerToInitialize(Future<Integer> installerExitCode) throws Exception {
try {
final long messageInterval = 30000L;
final long problemMessageInterval = 120000L;
long timerStart = System.currentTimeMillis();
long intervalStart = timerStart;
while (!isRHQServerInitialized()) {
Long now = System.currentTimeMillis();
if (installerExitCode.isDone() && installerExitCode.get().intValue() != RHQControl.EXIT_CODE_OK) {
stopServer();
throw new RuntimeException("Installer failed with code " + installerExitCode.get().intValue()
+ ", shut down server");
}
if ((now - intervalStart) > messageInterval) {
long totalWait = (now - timerStart);
if (totalWait < problemMessageInterval) {
log.info("Still waiting for server to start...");
} else {
long minutes = totalWait / 60000;
log.info("It has been over ["
+ minutes
+ "] minutes - you may want to ensure your server startup is proceeding as expected. You can check the log at ["
+ new File(getBaseDir(), "logs/server.log").getPath() + "].");
timerStart = now;
}
intervalStart = now;
}
Thread.sleep(5000);
}
} catch (IOException e) {
log.error("An error occurred while checking to see if the server is initialized: " + e.getMessage());
throw e;
} catch (InterruptedException e) {
// Don't think we need to log any details here
throw e;
}
}
protected ModelControllerClient getModelControllerClient() throws IOException {
ModelControllerClient mcc = null;
File propsFile = getServerPropertiesFile();
BufferedReader reader = new BufferedReader(new FileReader(propsFile));
Properties props = new Properties();
try {
props.load(reader);
} finally {
try {
reader.close();
} catch (Exception e) {
// best effort
}
}
String host = (String) props.get("jboss.bind.address.management");
if ("0.0.0.0".equals(host)) {
host = "127.0.0.1"; // use the loopback address if the management address is bound to all addressed (the client can't use 0.0.0.0)
}
int port = Integer.valueOf((String) props.get("jboss.management.native.port")).intValue();
mcc = MCCHelper.createModelControllerClient(host, port);
return mcc;
}
@SuppressWarnings("resource")
protected boolean isRHQServerInitialized() throws IOException {
BufferedReader reader = null;
ModelControllerClient mcc = null;
try {
mcc = getModelControllerClient();
DeploymentJBossASClient client = new DeploymentJBossASClient(mcc);
boolean isDeployed = client.isDeployment("rhq.ear");
return isDeployed;
} catch (Throwable t) {
log.debug("Falling back to logfile check due to: ", t);
File logDir = new File(getBaseDir(), "logs");
reader = new BufferedReader(new FileReader(new File(logDir, "server.log")));
String line = reader.readLine();
while (line != null) {
if (line.contains("Server initialized")) {
return true;
}
line = reader.readLine();
}
return false;
} finally {
MCCHelper.safeClose(mcc);
if (null != reader) {
try {
reader.close();
} catch (Exception e) {
// best effort
}
}
}
}
protected int updateWindowsAgentService(final File agentBasedir) throws Exception {
if (!isWindows()) {
return RHQControl.EXIT_CODE_OK;
}
int rValue = RHQControl.EXIT_CODE_OK;
try {
File agentBinDir = new File(agentBasedir, "bin");
if (!agentBinDir.exists()) {
throw new IllegalArgumentException("No Agent found for base directory [" + agentBasedir.getPath() + "]");
}
log.info("Updating RHQ Agent Service...");
org.apache.commons.exec.CommandLine commandLine;
// Ensure the windows service is up to date. [re-]install the windows service.
commandLine = getCommandLine("rhq-agent-wrapper", "stop");
try {
rValue = Math.max(rValue, ExecutorAssist.execute(agentBinDir, commandLine));
} catch (Exception e) {
// Ignore, service may not exist or be running, , script returns 1
log.debug("Failed to stop agent service", e);
}
commandLine = getCommandLine("rhq-agent-wrapper", "remove");
try {
rValue = Math.max(rValue, ExecutorAssist.execute(agentBinDir, commandLine));
} catch (Exception e) {
// Ignore, service may not exist, script returns 1
log.debug("Failed to uninstall agent service", e);
}
commandLine = getCommandLine("rhq-agent-wrapper", "install");
rValue = Math.max(rValue, ExecutorAssist.execute(agentBinDir, commandLine));
} catch (IOException e) {
log.error("An error occurred while updating the agent service: " + e.getMessage());
throw e;
}
return rValue;
}
protected int startAgent(final File agentBasedir) throws Exception {
int rValue = RHQControl.EXIT_CODE_OK;
try {
File agentBinDir = new File(agentBasedir, "bin");
if (!agentBinDir.exists()) {
throw new IllegalArgumentException("No Agent found for base directory [" + agentBasedir.getPath() + "]");
}
log.info("Starting RHQ agent...");
org.apache.commons.exec.CommandLine commandLine;
// For *nix, just start the server in the background, for Win, now that the service is installed, start it
commandLine = getCommandLine("rhq-agent-wrapper", "start");
rValue = Math.max(rValue, ExecutorAssist.execute(agentBinDir, commandLine));
// if any errors occur after now, we need to stop the agent
addUndoTask(new ControlCommand.UndoTask("Stopping agent") {
public void performUndoWork() throws Exception {
killAgent(agentBasedir);
}
});
log.info("The agent has started up");
} catch (IOException e) {
log.error("An error occurred while starting the agent: " + e.getMessage());
throw e;
}
return rValue;
}
protected int killAgent(File agentBasedir) throws Exception {
File agentBinDir = new File(agentBasedir, "bin");
if (!agentBinDir.exists()) {
throw new IllegalArgumentException("No Agent found for base directory [" + agentBasedir.getPath() + "]");
}
log.debug("Stopping RHQ agent...");
org.apache.commons.exec.CommandLine commandLine;
int rValue = RHQControl.EXIT_CODE_OK;
if (isWindows()) {
try {
commandLine = getCommandLine("rhq-agent-wrapper", "stop");
rValue = Math.max(rValue, ExecutorAssist.execute(agentBinDir, commandLine));
} catch (Exception e) {
// Ignore, service may not exist or be running, , script returns 1
log.debug("Failed to stop agent service", e);
rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
commandLine = getCommandLine("rhq-agent-wrapper", "kill");
rValue = Math.max(rValue, ExecutorAssist.execute(agentBinDir, commandLine));
}
return rValue;
}
protected int stopServer() throws Exception {
File serverBinDir = getBinDir();
if (!serverBinDir.exists()) {
throw new IllegalArgumentException("No Server found for base directory [" + getBaseDir().getPath() + "]");
}
log.debug("Stopping RHQ server...");
org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-server", "stop");
int rValue;
if (isWindows()) {
try {
rValue = ExecutorAssist.execute(serverBinDir, commandLine);
} catch (Exception e) {
// Ignore, service may not exist or be running, , script returns 1
log.debug("Failed to stop server service", e);
rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
rValue = ExecutorAssist.execute(serverBinDir, commandLine);
}
return rValue;
}
protected int startRHQServerForInstallation() throws IOException {
int rValue = RHQControl.EXIT_CODE_OK;
try {
validateServerPropertiesFile();
File serverPropsFile = getServerPropertiesFile();
Properties serverProps = new PropertiesFileUpdate(serverPropsFile).loadExistingProperties();
for (Map.Entry<Object, Object> entry : serverProps.entrySet()) {
String key = (String) entry.getKey();
if(key.startsWith("jboss.")) {
System.setProperty(key, (String) entry.getValue());
}
}
log.info("The RHQ Server must be started to complete its installation. Starting the RHQ server in preparation of running the server installer...");
// when you unzip the distro, you are getting a fresh, unadulterated, out-of-box EAP installation, which by default listens
// to port 9999 for its native management subsystem. Make sure some other independent EAP server (or anything for that matter)
// isn't already listening to that port.
String nativeManagementPortProperty = System.getProperty(PREF_JBOSS_MANAGEMENT_NATIVE_PORT);
int nativeManagementPort = 9999;
if(nativeManagementPortProperty != null) {
try {
nativeManagementPort = Integer.valueOf(nativeManagementPortProperty);
} catch(NumberFormatException nfe) {
log.error("Given " + PREF_JBOSS_MANAGEMENT_NATIVE_PORT + " could not be parsed as a valid port number, using default 9999 for installation");
}
}
if (isPortInUse("127.0.0.1", nativeManagementPort)) {
throw new IOException(
"Something is already listening to port " + nativeManagementPort + " - shut it down before installing the server.");
}
org.apache.commons.exec.CommandLine commandLine;
if (isWindows()) {
// For windows we will [re-]install the server as a windows service, then start the service.
commandLine = getCommandLine("rhq-server", "stop");
rValue = Math.max(rValue, ExecutorAssist.execute(getBinDir(), commandLine));
commandLine = getCommandLine("rhq-server", "remove");
rValue = Math.max(rValue, ExecutorAssist.execute(getBinDir(), commandLine));
commandLine = getCommandLine("rhq-server", "install");
rValue = Math.max(rValue, ExecutorAssist.execute(getBinDir(), commandLine));
commandLine = getCommandLine("rhq-server", "start");
rValue = Math.max(rValue, ExecutorAssist.execute(getBinDir(), commandLine));
} else {
// For *nix, just start the server in the background
commandLine = getCommandLine("rhq-server", "start");
ExecutorAssist.executeAsync(getBinDir(), commandLine, null);
}
addUndoTaskToStopComponent("--server"); // if any errors occur after now, we need to stop the server
// Wait for the server to complete it's startup
log.info("Waiting for the RHQ Server to start in preparation of running the server installer...");
commandLine = getCommandLine("rhq-installer", "--test");
int exitCode = 0;
int numTries = 0, maxTries = 30;
do {
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
// just keep going
}
if (numTries++ > maxTries) {
throw new IOException("Failed to detect server initialization, max tries exceeded. Aborting...");
}
if (numTries > 1) {
log.info("Still waiting to run the server installer...");
}
// for first 10 tries ignore what "rhq-installer --test" produces, only care about exit code
if (numTries < 10) {
exitCode = ExecutorAssist.getSilent().exec(getBinDir(), commandLine);
} else {
exitCode = ExecutorAssist.execute(getBinDir(), commandLine);
}
} while (exitCode != 0);
log.info("The RHQ Server is ready to be upgraded by the server installer.");
} catch (IOException e) {
log.error("An error occurred while starting the RHQ server: " + e.getMessage());
throw e;
}
return rValue;
}
protected enum ServerInstallerAction {
INSTALL("Installing"), UPGRADE("Upgrading"), UPDATESTORAGESCHEMA("Updating RHQ Storage Cluster schema"), LISTVERSIONS(
"Topology Versions"), CLEARCOLUMNFAMILIES("Clear inventory of obsolete column families");
public String display;
ServerInstallerAction(String display) {
this.display = display;
}
};
protected Future<Integer> runRHQServerInstaller(ServerInstallerAction serverInstallerAction) throws Exception {
Future<Integer> integerFuture = null;
switch (serverInstallerAction) {
case INSTALL:
case UPGRADE: {
log.info(serverInstallerAction.display + " RHQ Server");
// If the install fails, we will remove the install marker file allowing the installer to be able to run again.
// We also need to revert mgmt-users.properties
File mgmtUserPropertiesFile = new File(getBaseDir(),
"jbossas/standalone/configuration/mgmt-users.properties");
File standaloneXmlFile = new File(getBaseDir(), "jbossas/standalone/configuration/standalone-full.xml");
final FileReverter mgmtUserPropertiesReverter = new FileReverter(mgmtUserPropertiesFile);
final FileReverter standaloneXmlFileReverter = new FileReverter(standaloneXmlFile);
addUndoTask(new ControlCommand.UndoTask(
"Removing server-installed marker file and management user and reverting to original standalone-full.xml") {
public void performUndoWork() throws Exception {
getServerInstalledMarkerFile(getBaseDir()).delete();
mgmtUserPropertiesReverter.revert();
standaloneXmlFileReverter.revert();
}
});
org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-installer");
if (ServerInstallerAction.UPGRADE == serverInstallerAction) {
commandLine.addArgument("--upgrade");
}
integerFuture = ExecutorAssist.executeAsync(getBinDir(), commandLine, null);
log.info("The RHQ Server installer is running");
break;
}
case CLEARCOLUMNFAMILIES: {
log.info(serverInstallerAction.display);
org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-installer", "--clearcolumnfamilies");
integerFuture = ExecutorAssist.executeAsync(getBinDir(), commandLine, null);
log.info("The RHQ Server column family cleaning is running");
break;
}
case UPDATESTORAGESCHEMA: {
log.info(serverInstallerAction.display);
org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-installer", "--updatestorageschema");
integerFuture = ExecutorAssist.executeAsync(getBinDir(), commandLine, null);
log.info("The RHQ Storage Cluster schema update is running");
break;
}
case LISTVERSIONS: {
log.info(serverInstallerAction.display);
org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-installer", "--listversions");
integerFuture = ExecutorAssist.executeAsync(getBinDir(), commandLine, null);
break;
}
}
return integerFuture;
}
private class StorageDataDirectories {
public File basedir; // the other three will typically be under this base directory
public File dataDir;
public File commitlogDir;
public File savedcachesDir;
}
private StorageDataDirectories getCustomStorageDataDirectories(CommandLine commandLine) {
StorageDataDirectories storageDataDirs = null;
if (commandLine.hasOption(STORAGE_DATA_ROOT_DIR)) {
storageDataDirs = new StorageDataDirectories();
storageDataDirs.basedir = new File(commandLine.getOptionValue(STORAGE_DATA_ROOT_DIR));
storageDataDirs.dataDir = new File(storageDataDirs.basedir, "data");
storageDataDirs.commitlogDir = new File(storageDataDirs.basedir, "commit_log");
storageDataDirs.savedcachesDir = new File(storageDataDirs.basedir, "saved_caches");
}
return storageDataDirs;
}
private StorageDataDirectories getStorageDataDirectoriesFromProperties(Properties storageProperties) {
File basedirForData = new File(getBaseDir().getParentFile(), "rhq-data");
// check that the data directories are set and convert to absolute dirs
File dataDirProp = new File(storageProperties.getProperty("rhq.storage.data", "data"));
File commitlogDirProp = new File(storageProperties.getProperty("rhq.storage.commitlog", "commit_log"));
File savedcachesDirProp = new File(storageProperties.getProperty("rhq.storage.saved-caches", "saved_caches"));
if (!dataDirProp.isAbsolute()) {
dataDirProp = new File(basedirForData, dataDirProp.getPath());
}
if (!commitlogDirProp.isAbsolute()) {
commitlogDirProp = new File(basedirForData, commitlogDirProp.getPath());
}
if (!savedcachesDirProp.isAbsolute()) {
savedcachesDirProp = new File(basedirForData, savedcachesDirProp.getPath());
}
StorageDataDirectories storageDataDirs = new StorageDataDirectories();
storageDataDirs.basedir = basedirForData;
storageDataDirs.dataDir = dataDirProp;
storageDataDirs.commitlogDir = commitlogDirProp;
storageDataDirs.savedcachesDir = savedcachesDirProp;
return storageDataDirs;
}
protected int installStorageNode(final File storageBasedir, CommandLine rhqctlCommandLine, boolean start)
throws Exception {
try {
log.info("Preparing to install RHQ storage node.");
final Properties storageProperties = loadStorageProperties();
org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-storage-installer", "--dir",
storageBasedir.getAbsolutePath(), "--start", Boolean.valueOf(start).toString());
if (rhqctlCommandLine.hasOption(STORAGE_DATA_ROOT_DIR)) {
StorageDataDirectories dataDirs;
dataDirs = getCustomStorageDataDirectories(rhqctlCommandLine);
storageProperties.setProperty("rhq.storage.data", dataDirs.dataDir.getAbsolutePath());
storageProperties.setProperty("rhq.storage.commitlog", dataDirs.commitlogDir.getAbsolutePath());
storageProperties.setProperty("rhq.storage.saved-caches", dataDirs.savedcachesDir.getAbsolutePath());
}
// add the properties set in rhq-storage.properties to the command line
String[] args = toArray(storageProperties);
commandLine.addArguments(args);
// if the install fails, we need to delete the data directories that were created and
// purge the rhq-storage install directory that might have a "half" installed storage node in it.
addUndoTask(new ControlCommand.UndoTask("Removing storage node data and install directories") {
public void performUndoWork() {
StorageDataDirectories dataDirs = getStorageDataDirectoriesFromProperties(storageProperties);
FileUtil.purge(dataDirs.dataDir, true);
FileUtil.purge(dataDirs.commitlogDir, true);
FileUtil.purge(dataDirs.savedcachesDir, true);
FileUtil.purge(storageBasedir, true);
}
});
// execute the storage installer now
int exitCode = ExecutorAssist.execute(getBinDir(), commandLine);
log.info("The storage node installer has finished with an exit value of " + exitCode);
// the storage node is installed AND running now so, if we fail later, we need to shut the storage node down
addUndoTaskToStopComponent("--storage");
return exitCode;
} catch (Exception e) {
log.error("An error occurred while running the storage installer: " + e.getMessage());
if (e.getMessage().toLowerCase().contains("exit value: 3")) {
log.error("Try to point your root data directory via --" + STORAGE_DATA_ROOT_DIR
+ " to a directory where you have read and write permissions.");
}
throw e;
}
}
protected int installAgent(final File agentBasedir, final CommandLine commandLine) throws Exception {
clearAgentPreferences();
int rValue = installAgent(agentBasedir);
configureAgent(agentBasedir, commandLine);
return rValue;
}
private int installAgent(final File agentBasedir) throws Exception {
try {
log.info("Installing RHQ agent");
File agentInstallerJar = getAgentInstaller();
setAgentBasedir(agentBasedir);
// if the install fails, we will completely delete any agent that might have been "half" installed
addUndoTask(new ControlCommand.UndoTask("Removing agent install directory") {
public void performUndoWork() {
FileUtil.purge(agentBasedir, true);
}
});
// Make sure we use the appropriate java version, don't just fall back to PATH
String javaExeFilePath = System.getProperty("rhq.java-exe-file-path");
org.apache.commons.exec.CommandLine commandLine = new org.apache.commons.exec.CommandLine(javaExeFilePath)
.addArgument("-jar").addArgument(agentInstallerJar.getAbsolutePath())
.addArgument("--install=" + agentBasedir.getParentFile().getAbsolutePath())
.addArgument("--log=" + new File(getLogDir(), "rhq-agent-update.log"));
int exitValue = ExecutorAssist.execute(getBaseDir(), commandLine);
log.info("The agent installer finished running with exit value " + exitValue);
return exitValue;
} catch (Exception e) {
log.error("An error occurred while running the agent installer: " + e.getMessage());
throw e;
}
}
private File getAgentInstaller() {
File agentDownloadDir = new File(getBaseDir(),
"modules/org/rhq/server-startup/main/deployments/rhq.ear/rhq-downloads/rhq-agent");
return agentDownloadDir.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return file.getName().contains("rhq-enterprise-agent");
}
})[0];
}
private void clearAgentPreferences() throws Exception {
log.info("Removing any existing agent preferences from default preference node");
// remove everything EXCEPT the security token
Preferences agentPrefs = getAgentPreferences();
String[] prefKeys = null;
try {
prefKeys = agentPrefs.keys();
} catch (Exception e) {
log.warn("Failed to get agent preferences - cannot clear them: " + e);
}
if (prefKeys != null && prefKeys.length > 0) {
for (String prefKey : prefKeys) {
if (!prefKey.equals(PREF_RHQ_AGENT_SECURITY_TOKEN)) {
agentPrefs.remove(prefKey);
}
}
agentPrefs.flush();
Preferences.userRoot().sync();
}
}
protected int updateAndMoveExistingAgent(final File agentBasedir, final File oldAgentDir,
final File agentInstallerJar) throws Exception {
// Make sure we use the appropriate java version, don't just fall back to PATH
String javaExeFilePath = System.getProperty("rhq.java-exe-file-path");
org.apache.commons.exec.CommandLine commandLine = new org.apache.commons.exec.CommandLine(javaExeFilePath) //
.addArgument("-jar").addArgument(agentInstallerJar.getAbsolutePath()) //
.addArgument("--update=" + oldAgentDir.getAbsolutePath()) //
.addArgument("--log=" + new File(getLogDir(), "rhq-agent-update.log")) //
.addArgument("--launch=false"); // we can't launch this copy - we still have to move it to the new location
int exitValue = ExecutorAssist.execute(getBaseDir(), commandLine);
log.info("The agent installer finished updating with exit value " + exitValue);
// We need to now move the new, updated agent over to the new agent location.
// renameTo() may fail if we are crossing file system boundaries, so try a true copy as a fallback.
if (!agentBasedir.equals(oldAgentDir)) {
// BZ 1118906 - we need to guard against the possibility that one or both of these are symlinks which aren't
// "equal" to each other but yet still point to the same location. If they point to the same location
// it is as if they are "equal" and we should do nothing.
if (!agentBasedir.getCanonicalPath().equals(oldAgentDir.getCanonicalPath())) {
FileUtil.purge(agentBasedir, true); // clear the way for the new upgraded agent
if (!oldAgentDir.renameTo(agentBasedir)) {
FileUtil.copyDirectory(oldAgentDir, agentBasedir);
// we need to retain the execute bits for the executable scripts and libraries
FileVisitor visitor = new FileVisitor() {
@Override
public void visit(File file) {
String filename = file.getName();
if (filename.contains(".so") || filename.contains(".sl") || filename.contains(".dylib")) {
file.setExecutable(true);
} else if (filename.endsWith(".sh")) {
file.setExecutable(true);
}
}
};
FileUtil.forEachFile(new File(agentBasedir, "bin"), visitor);
FileUtil.forEachFile(new File(agentBasedir, "lib"), visitor);
}
}
}
return exitValue;
}
static protected File getFromAgentDir(CommandLine commandLine) {
return (commandLine.hasOption(FROM_AGENT_DIR_OPTION)) ? new File(
commandLine.getOptionValue(FROM_AGENT_DIR_OPTION)) : null;
}
protected File getFileDownload(String directory, final String fileMatch) {
File downloadDir = new File(getBaseDir(),
"modules/org/rhq/server-startup/main/deployments/rhq.ear/rhq-downloads/" + directory);
return downloadDir.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return file.getName().contains(fileMatch);
}
})[0];
}
private Preferences getAgentPreferences() {
Preferences agentPrefs = Preferences.userRoot().node("rhq-agent/default");
return agentPrefs;
}
private void configureAgent(File agentBasedir, CommandLine commandLine) throws Exception {
// If the user provided us with an agent config file, we will use it.
// Otherwise, we are going to use the out-of-box agent config file.
//
// Because we want to accept all defaults and consider the agent fully configured, we need to set
// rhq.agent.configuration-setup-flag=true
// This tells the agent not to ask any setup questions at startup.
// We do this whether using a custom config file or the default config file - this is because
// we cannot allow the agent to ask the setup questions (rhqctl doesn't support that).
//
// Note that agent preferences found in the config file can be overridden with
// the AGENT_PREFERENCE settings (you can set more than one).
try {
File agentConfDir = new File(agentBasedir, "conf");
File agentConfigFile = new File(agentConfDir, "agent-configuration.xml");
// BZ 1158228 - to support install on Windows, make sure the first time we reload the agent config xml always
File agentConfigReloadMarkerFile = new File(agentConfigFile.getAbsolutePath() + ".reload");
agentConfigReloadMarkerFile.createNewFile();
if (commandLine.hasOption(AGENT_CONFIG_OPTION)) {
log.info("Configuring the RHQ agent with custom configuration file: "
+ commandLine.getOptionValue(AGENT_CONFIG_OPTION));
replaceAgentConfigIfNecessary(commandLine);
} else {
log.info("Configuring the RHQ agent with default configuration file: " + agentConfigFile);
}
// we require our agent preference node to be the user node called "default"
Preferences preferencesNode = getAgentPreferences();
// read the comments in AgentMain.loadConfigurationFile(String) to know why we do all of this
String securityToken = preferencesNode.get(PREF_RHQ_AGENT_SECURITY_TOKEN, null);
ByteArrayOutputStream rawConfigFileData = new ByteArrayOutputStream();
StreamUtil.copy(new FileInputStream(agentConfigFile), rawConfigFileData, true);
String newConfig = rawConfigFileData.toString().replace("${rhq.agent.preferences-node}", "default");
ByteArrayInputStream newConfigInputStream = new ByteArrayInputStream(newConfig.getBytes());
Preferences.importPreferences(newConfigInputStream);
if (securityToken != null) {
preferencesNode.put(PREF_RHQ_AGENT_SECURITY_TOKEN, securityToken);
}
// get the configured server endpoint information and tell the agent so it knows where the server is.
Properties serverEndpoint = getAgentServerEndpoint();
String endpointTransport = serverEndpoint.getProperty(PREF_RHQ_AGENT_SERVER_TRANSPORT);
String endpointAddress = serverEndpoint.getProperty(PREF_RHQ_AGENT_SERVER_BINDADDRESS);
String endpointPort = serverEndpoint.getProperty(PREF_RHQ_AGENT_SERVER_BINDPORT);
String endpointParams = serverEndpoint.getProperty(PREF_RHQ_AGENT_SERVER_TRANSPORTPARAMS);
if (endpointTransport != null) {
preferencesNode.put(PREF_RHQ_AGENT_SERVER_TRANSPORT, endpointTransport);
}
if (endpointAddress != null) {
preferencesNode.put(PREF_RHQ_AGENT_SERVER_BINDADDRESS, endpointAddress);
}
if (endpointPort != null) {
preferencesNode.put(PREF_RHQ_AGENT_SERVER_BINDPORT, endpointPort);
}
if (endpointParams != null) {
preferencesNode.put(PREF_RHQ_AGENT_SERVER_TRANSPORTPARAMS, endpointParams);
}
// if the user provided any overrides to the agent config, use them.
overrideAgentPreferences(commandLine, preferencesNode);
// set some prefs that must be a specific value
// - do not tell this agent to auto-update itself - this agent must be managed by rhqctl only
// - set the config setup flag to true to prohibit the agent from asking setup questions at startup
String agentUpdateEnabledPref = PREF_RHQ_AGENT_AUTO_UPDATE_FLAG;
preferencesNode.putBoolean(agentUpdateEnabledPref, false);
String setupPref = PREF_RHQ_AGENT_CONFIGURATION_SETUP_FLAG;
preferencesNode.putBoolean(setupPref, true);
try {
preferencesNode.flush();
preferencesNode.sync();
} catch (BackingStoreException bse) {
log.error("Failed to store agent preferences, for Linux systems we require writable user.home ["
+ System.getProperty("user.home")
+ "]. You can also set different location for agent preferences by setting \"-Djava.util.prefs.userRoot=/some/path/\""
+ " java system property. You may need to put this property to RHQ_CONTROL_ADDIDIONAL_JAVA_OPTS and RHQ_AGENT_ADDIDIONAL_JAVA_OPTS env variables.");
throw bse;
}
log.info("Finished configuring the agent");
} catch (Exception e) {
log.error("An error occurred while configuring the agent: " + e.getMessage());
throw e;
}
}
/**
* Returns the server endpoint that the agent should use when connecting to the server.
*
* @return properties with the values - only those values that could be determined will be in here
*
* @throws Exception if could not determine the values due to a runtime error
*/
private Properties getAgentServerEndpoint() throws Exception {
Properties endpointData = new Properties();
// load in the server properties file to find out how the server will be listening for agent messages
File serverPropsFile = getServerPropertiesFile();
Properties serverProps = new PropertiesFileUpdate(serverPropsFile).loadExistingProperties();
// transport and transport params
String transport = serverProps.getProperty("rhq.communications.connector.transport", "servlet");
endpointData.put(PREF_RHQ_AGENT_SERVER_TRANSPORT, transport);
String params = serverProps.getProperty("rhq.communications.connector.transport-params");
if (params != null && params.trim().length() > 0) {
endpointData.setProperty(PREF_RHQ_AGENT_SERVER_TRANSPORTPARAMS, params.trim());
}
// address and port depends on the transport
if (transport.contains("servlet")) {
String port;
if (transport.contains("ssl")) {
port = serverProps.getProperty("rhq.server.socket.binding.port.https");
} else {
port = serverProps.getProperty("rhq.server.socket.binding.port.http");
}
if (port != null && port.trim().length() > 0) {
endpointData.setProperty(PREF_RHQ_AGENT_SERVER_BINDPORT, port.trim());
}
String address = serverProps.getProperty("jboss.bind.address");
if (address != null && address.trim().length() > 0) {
endpointData.setProperty(PREF_RHQ_AGENT_SERVER_BINDADDRESS, address.trim());
}
} else {
String port = serverProps.getProperty("rhq.communications.connector.bind-port");
if (port != null && port.trim().length() > 0) {
endpointData.setProperty(PREF_RHQ_AGENT_SERVER_BINDPORT, port.trim());
}
String address = serverProps.getProperty("rhq.communications.connector.bind-address");
if (address != null && address.trim().length() > 0) {
endpointData.setProperty(PREF_RHQ_AGENT_SERVER_BINDADDRESS, address.trim());
}
}
// the public endpoint settings will override anything that was done above
String publicEndpoint = serverProps.getProperty("rhq.autoinstall.public-endpoint-address");
if (publicEndpoint != null && publicEndpoint.trim().length() > 0) {
endpointData.setProperty(PREF_RHQ_AGENT_SERVER_BINDADDRESS, publicEndpoint.trim());
}
String publicPort;
if (transport.contains("ssl")) {
publicPort = serverProps.getProperty("rhq.autoinstall.public-endpoint-secure-port");
} else {
publicPort = serverProps.getProperty("rhq.autoinstall.public-endpoint-port");
}
if (publicPort != null && publicPort.trim().length() > 0) {
endpointData.setProperty(PREF_RHQ_AGENT_SERVER_BINDPORT, publicPort.trim());
}
return endpointData;
}
private void overrideAgentPreferences(CommandLine commandLine, Preferences preferencesNode) throws BackingStoreException, IOException {
// override the out of box config with user custom agent preference values
String[] customPrefs = commandLine.getOptionValues(AGENT_PREFERENCE);
if (customPrefs != null && customPrefs.length > 0) {
for (String nameValuePairString : customPrefs) {
String[] nameValuePairArray = nameValuePairString.split("=", 2);
String prefName = nameValuePairArray[0];
String prefValue = nameValuePairArray.length == 1 ? "true" : nameValuePairArray[1];
log.info("Overriding agent preference: " + prefName + "=" + prefValue);
preferencesNode.put(prefName, prefValue);
}
}
// These should be stored in the configuration file also for reload marker
File confDir = new File(getAgentBasedir(), "conf");
File defaultConfigFile = new File(confDir, "agent-configuration.xml");
File backupConfigFile = new File(confDir, "agent-configuration.xml.orig");
if(!defaultConfigFile.renameTo(backupConfigFile)) {
log.error("Could not rename " + defaultConfigFile.getAbsolutePath() + " to " + backupConfigFile.getAbsolutePath() + " for backup purposes");
}
FileOutputStream fos = null;
try {
fos = new FileOutputStream(defaultConfigFile);
preferencesNode.exportSubtree(fos);
} catch (IOException e) {
log.error("Failed to store overridden agent preferences to " + defaultConfigFile.getAbsolutePath() + ".");
throw e;
} catch (BackingStoreException e) {
log.error("Failed to store overridden agent preferences to " + defaultConfigFile.getAbsolutePath() + ".");
throw e;
} finally {
if(fos != null) {
fos.close();
}
}
return;
}
private void replaceAgentConfigIfNecessary(CommandLine commandLine) {
if (!commandLine.hasOption(AGENT_CONFIG_OPTION)) {
return;
}
File newConfigFile = new File(commandLine.getOptionValue(AGENT_CONFIG_OPTION));
File confDir = new File(getAgentBasedir(), "conf");
File defaultConfigFile = new File(confDir, "agent-configuration.xml");
defaultConfigFile.delete();
try {
StreamUtil.copy(new FileReader(newConfigFile), new FileWriter(defaultConfigFile));
} catch (IOException e) {
throw new RHQControlException(("Failed to replace " + defaultConfigFile + " with " + newConfigFile));
}
}
protected void addUndoTaskToStopComponent(final String componentArgument) {
// component argument must be one of --storage, --server, --agent (a valid argument to the Stop command)
addUndoTask(new ControlCommand.UndoTask("Stopping component: " + componentArgument) {
public void performUndoWork() throws Exception {
Stop stopCommand = new Stop();
CommandLineParser parser = new RHQPosixParser(true);
CommandLine cmdLine = parser.parse(stopCommand.getOptions(), new String[] { componentArgument });
stopCommand.exec(cmdLine);
}
});
}
private Properties loadStorageProperties() throws IOException {
Properties properties = new Properties();
FileInputStream fis = null;
try {
fis = new FileInputStream(new File("bin/rhq-storage.properties"));
properties.load(fis);
// Ignore empty values
Iterator<Map.Entry<Object, Object>> iterator = properties.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Object, Object> entry = iterator.next();
String value = (String) entry.getValue();
if (value == null || value.length() < 1) {
iterator.remove();
}
}
} finally {
if (null != fis) {
fis.close();
}
}
return properties;
}
private String[] toArray(Properties properties) {
String[] array = new String[properties.size() * 2];
int i = 0;
for (Object key : properties.keySet()) {
array[i++] = "--" + (String) key;
array[i++] = properties.getProperty((String) key);
}
return array;
}
}