
org.rhq.server.control.RHQControl Maven / Gradle / Ivy
The newest version!
/*
*
* * RHQ Management Platform
* * Copyright (C) 2005-2012 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;
import java.io.Console;
import java.io.File;
import java.io.FilenameFilter;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.crypto.CryptoUtil;
import org.rhq.core.util.PropertiesFileUpdate;
import org.rhq.core.util.StringUtil;
import org.rhq.core.util.exception.ThrowableUtil;
import org.rhq.core.util.obfuscation.Obfuscator;
import org.rhq.enterprise.server.installer.ServerProperties;
import org.rhq.storage.installer.StorageProperty;
/**
* @author John Sanda
*/
public class RHQControl {
private final Log log = LogFactory.getLog(RHQControl.class);
public static final int EXIT_CODE_OK = 0;
// These try to follow the LSB specification - status command
public static final int EXIT_CODE_STATUS_NOT_RUNNING = 3;
public static final int EXIT_CODE_STATUS_UNKNOWN = 4;
// These try to follow the LSB specification - other commands
public static final int EXIT_CODE_OPERATION_FAILED = 1;
public static final int EXIT_CODE_INVALID_ARGUMENT = 2;
public static final int EXIT_CODE_NOT_INSTALLED = 5;
private Commands commands = new Commands();
public void printUsage() {
HelpFormatter helpFormatter = new HelpFormatter();
String syntax = "rhqctl [options]";
String header = "\nwhere is one of:";
String footer = "\n* For help on a specific command: rhqctl --help\n" //
+ "\n* Limit commands to a single component with one of: --storage, --server, --agent";
helpFormatter.setOptPrefix("");
helpFormatter.printHelp(syntax, header, commands.getOptions(), footer);
}
public int exec(String[] args) {
int rValue = EXIT_CODE_OK;
ControlCommand command = null;
boolean undo = false;
AbortHook abortHook = new AbortHook();
try {
if (args.length == 0) {
printUsage();
rValue = EXIT_CODE_INVALID_ARGUMENT;
} else {
String commandName = findCommand(commands, args);
command = commands.get(commandName);
if (!isHelp(args)) {
// don't wait for user to read the warning if this is just request for help
logWarningIfAgentRPMIsInstalled(command);
}
validateInstallCommand(command, args);
// in case the installer gets killed, prepare the shutdown hook to try the undo
abortHook.setCommand(command);
Runtime.getRuntime().addShutdownHook(abortHook);
// run the command
rValue = command.exec(getCommandLine(commandName, args));
}
} catch (UsageException e) {
printUsage();
rValue = EXIT_CODE_INVALID_ARGUMENT;
} catch (RHQControlException e) {
undo = true;
Throwable rootCause = ThrowableUtil.getRootCause(e);
// Only show the messy stack trace if we're in debug mode. Otherwise keep it cleaner for the user...
if (log.isDebugEnabled()) {
log.error(rootCause.getMessage(), rootCause);
} else {
log.error(rootCause.getMessage());
}
rValue = EXIT_CODE_OPERATION_FAILED;
} catch (Throwable t) {
undo = true;
log.error(t);
rValue = EXIT_CODE_OPERATION_FAILED;
} finally {
abortHook.setCommand(null);
Runtime.getRuntime().removeShutdownHook(abortHook);
}
if (undo && command != null) {
try {
if (!Boolean.getBoolean("rhqctl.skip.undo")) {
command.undo();
} else {
throw new Exception("Was told by user to skip clean up attempt.");
}
} catch (Throwable t) {
log.warn("Failed to clean up after the failed installation attempt. "
+ "You may have to clean up some things before attempting to install again", t);
rValue = EXIT_CODE_OPERATION_FAILED;
}
}
return rValue;
}
private void logWarningIfAgentRPMIsInstalled(ControlCommand command) {
// we only care about warning if the user is installing or upgrading; otherwise, just return silently
if (!"install".equalsIgnoreCase(command.getName()) && (!"upgrade".equalsIgnoreCase(command.getName()))) {
return;
}
// see if we can find an RPM installation somewhere.
boolean rpmInstalled;
File rpmParentLocation = new File("/usr/share");
File[] rpms = rpmParentLocation.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
if (name.startsWith("jboss-on-")) {
File jonDir = new File(dir, name);
// technically, we should look for the agent in new File(jonDir, "agent") because that's the rpm install dir.
// but there are no other rpms other than the agent, so if we see this jboss-on-* location, we know the agent is here.
log.warn("An agent RPM installation was found in ["
+ jonDir
+ "]!!! You will not be able to successfully run this older agent anymore. You should consult the "
+ "install documentation about manually removing and/or merging the old and new agent.");
return true;
} else {
return false;
}
}
});
// if there is an RPM install on this box, we need to pause for some time to give the user a chance to read the message
rpmInstalled = (rpms != null && rpms.length > 0);
if (rpmInstalled) {
try {
log.warn("Please read the above warnings about the existence of agent RPM installations. This "
+ command.getName() + " will resume in a few seconds.");
Thread.sleep(30000L);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
return;
}
private void validateInstallCommand(ControlCommand command, String[] args) {
// just return if we're asking for help or if it is not an install command
if (!"install".equalsIgnoreCase(command.getName()) || isHelp(args)) {
return;
}
List argsList = Arrays.asList(args);
// don't perform validation for components not involved in the command
boolean validateServer = argsList.contains("--server")
|| (!argsList.contains("--storage") && !argsList.contains("--agent"));
boolean validateStorage = argsList.contains("--storage")
|| (!argsList.contains("--server") && !argsList.contains("--agent"));
// perform any up front validation we can at this point. Note that after this point we
// lose stdin due to the use of ProcessExecutions.
if (validateServer) {
File serverPropertiesFile = new File("bin/rhq-server.properties");
if (validateServer && !serverPropertiesFile.isFile()) {
throw new RHQControlException(
"The required rhq-server.properties file can not be found in the expected location ["
+ serverPropertiesFile.getAbsolutePath() + "]. Installation is canceled.");
}
// Prompt for critical required values, if not yet set.
try {
PropertiesFileUpdate pfu = new PropertiesFileUpdate(serverPropertiesFile);
Properties props = pfu.loadExistingProperties();
promptForProperty(pfu, props, serverPropertiesFile.getName(),
ServerProperties.PROP_AUTOINSTALL_ADMIN_PASSWORD, false, true, true);
promptForProperty(pfu, props, serverPropertiesFile.getName(), ServerProperties.PROP_DATABASE_PASSWORD,
true, false, true);
promptForProperty(pfu, props, serverPropertiesFile.getName(), ServerProperties.PROP_JBOSS_BIND_ADDRESS,
false, false, false);
} catch (Throwable t) {
throw new RHQControlException("The rhq-server.properties file is not valid. Installation is canceled: "
+ t.getMessage());
}
// Now, validate the property settings
try {
ServerProperties.validate(serverPropertiesFile);
} catch (Throwable t) {
throw new RHQControlException("The rhq-server.properties file is not valid. Installation is canceled: "
+ t.getMessage());
}
}
if (validateStorage) {
try {
File storagePropertiesFile = new File("bin/rhq-storage.properties");
if (validateStorage && !storagePropertiesFile.isFile()) {
throw new RHQControlException(
"The required rhq-storage.properties file can not be found in the expected location ["
+ storagePropertiesFile.getAbsolutePath() + "]. Installation is canceled.");
}
StorageProperty.validate(storagePropertiesFile);
} catch (Throwable t) {
throw new RHQControlException(
"The rhq-storage.properties file is not valid. Installation is canceled: " + t.getMessage());
}
}
}
private void promptForProperty(PropertiesFileUpdate pfu, Properties props, String propertiesFileName,
String propertyName, boolean obfuscate, boolean encode, boolean hideInput) throws Exception {
String propertyValue = props.getProperty(propertyName);
if (StringUtil.isBlank(propertyValue)) {
// prompt for the property value
Console console = System.console();
console.format("\nThe [%s] property is required but not set in [%s].\n", propertyName, propertiesFileName);
console.format("Do you want to set [%s] value now?\n", propertyName);
String response = "";
while (!(response.startsWith("n") || response.startsWith("y"))) {
response = String.valueOf(console.readLine("%s", "yes|no: ")).toLowerCase();
}
if (response.startsWith("n")) {
throw new RHQControlException("Please update the [" + propertiesFileName + "] file as required.");
}
do {
propertyValue = "";
while (StringUtil.isBlank(propertyValue)) {
if (!hideInput) {
propertyValue = String.valueOf(console.readLine("%s", propertyName
+ (((obfuscate || encode) ? " (enter as plain text): " : ": "))));
} else {
propertyValue = String.valueOf(console.readPassword("%s", propertyName
+ (((obfuscate || encode) ? " (enter as plain text): " : ": "))));
}
}
if (!hideInput) {
console.format("Is [" + propertyValue + "] correct?\n");
response = "";
while (!(response.startsWith("n") || response.startsWith("y"))) {
response = String.valueOf(console.readLine("%s", "yes|no: ")).toLowerCase();
}
} else {
console.format("Confirm:\n");
String confirmValue = String.valueOf(console.readPassword("%s", propertyName
+ (((obfuscate || encode) ? " (enter as plain text): " : ": "))));
if (propertyValue.equals(confirmValue)) {
response = "yes";
} else {
response = "no";
console.printf("Did not match. Please try again.\n");
}
}
} while (response.startsWith("n"));
propertyValue = obfuscate ? Obfuscator.encode(propertyValue) : propertyValue;
propertyValue = encode ? CryptoUtil.createPasswordHash("MD5", CryptoUtil.BASE64_ENCODING, null, null,
propertyValue) : propertyValue;
props.setProperty(propertyName, propertyValue);
pfu.update(props);
}
}
private String findCommand(Commands commands, String[] args) throws RHQControlException {
List commandNames = new LinkedList();
for (String arg : args) {
if (commands.contains(arg)) {
commandNames.add(arg);
}
}
if (commandNames.size() != 1) {
throw new UsageException();
}
return commandNames.get(0);
}
private boolean isHelp(String[] args) {
for (String arg : args) {
if (ControlCommand.HELP_OPTION_1.equals(arg) || ControlCommand.HELP_OPTION_2.equals(arg)) {
return true;
}
}
return false;
}
private String[] getCommandLine(String cmd, String[] args) {
String[] cmdLine = new String[args.length - 1];
int i = 0;
for (String arg : args) {
if (arg.equals(cmd)) {
continue;
}
cmdLine[i++] = arg;
}
return cmdLine;
}
public static void main(String[] args) throws Exception {
RHQControl control = new RHQControl();
int rValue;
try {
rValue = control.exec(args);
} catch (RHQControlException e) {
Throwable rootCause = ThrowableUtil.getRootCause(e);
// Only show the messy stack trace if we're in debug mode. Otherwise keep it cleaner for the user...
if (control.log.isDebugEnabled()) {
control.log.error("There was an unexpected error: " + rootCause.getMessage(), rootCause);
} else {
control.log.error("There was an unexpected error: " + rootCause.getMessage());
}
rValue = EXIT_CODE_OPERATION_FAILED;
}
System.exit(rValue);
}
private class AbortHook extends Thread {
private ControlCommand command = null;
public AbortHook() {
super("Controller Abort Hook");
}
public void setCommand(ControlCommand cmd) {
this.command = cmd;
}
@Override
public void run() {
try {
if (this.command != null) {
this.command.undo();
}
} catch (Throwable t) {
log.warn("An attempt to clean up after an aborted installation was unsuccessful. "
+ "You may have to clean up some things before attempting to install again", t);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy