org.rhq.plugins.agent.AgentJavaServiceWrapperComponent Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rhq-agent-plugin Show documentation
Show all versions of rhq-agent-plugin Show documentation
a plugin for monitoring RHQ agents
/*
* RHQ Management Platform
* Copyright (C) 2005-2008 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 as published by
* the Free Software Foundation version 2 of the License.
*
* 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.plugins.agent;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.ConfigurationUpdateStatus;
import org.rhq.core.domain.configuration.Property;
import org.rhq.core.domain.configuration.PropertyList;
import org.rhq.core.domain.configuration.PropertyMap;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.pluginapi.configuration.ConfigurationFacet;
import org.rhq.core.pluginapi.configuration.ConfigurationUpdateReport;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceComponent;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.operation.OperationFacet;
import org.rhq.core.pluginapi.operation.OperationResult;
import org.rhq.core.system.ProcessExecution;
import org.rhq.core.system.ProcessExecutionResults;
import org.rhq.core.system.SystemInfo;
import org.rhq.core.util.exception.ExceptionPackage;
import org.rhq.core.util.exception.Severity;
import org.rhq.enterprise.agent.EnvironmentScriptFileUpdate;
import org.rhq.enterprise.agent.EnvironmentScriptFileUpdate.NameValuePair;
/**
* The component that represents the agent's Java Service Wrapper (JSW).
*
* @author John Mazzitelli
*/
public class AgentJavaServiceWrapperComponent implements ResourceComponent>,
ConfigurationFacet, OperationFacet {
private Log log = LogFactory.getLog(AgentJavaServiceWrapperComponent.class);
private ResourceContext> resourceContext;
private File launcherScript;
private File configFile;
private File environmentFile;
private File includeFile;
public void start(ResourceContext> rc) throws Exception {
this.resourceContext = rc;
Configuration pc = this.resourceContext.getPluginConfiguration();
PropertySimple prop;
prop = pc.getSimple(AgentJavaServiceWrapperDiscoveryComponent.PLUGINCONFIG_LAUNCHER_SCRIPT);
if (prop == null) {
throw new InvalidPluginConfigurationException("Missing Launcher Script");
}
if (prop.getStringValue() == null) {
throw new InvalidPluginConfigurationException("Launcher Script property value is null");
}
launcherScript = new File(prop.getStringValue());
if (!launcherScript.exists()) {
throw new InvalidPluginConfigurationException("Launcher Script [" + launcherScript + "] does not exist");
}
prop = pc.getSimple(AgentJavaServiceWrapperDiscoveryComponent.PLUGINCONFIG_CONF_FILE);
if (prop == null) {
throw new InvalidPluginConfigurationException("Missing Configuration File");
}
if (prop.getStringValue() == null) {
throw new InvalidPluginConfigurationException("Configuration File property value is null");
}
configFile = new File(prop.getStringValue());
if (!configFile.exists()) {
throw new InvalidPluginConfigurationException("Config file [" + configFile + "] does not exist");
}
log.debug("Starting agent JSW component: " + configFile);
// get the optional files (these may remain null if the paths were left undefined)
prop = pc.getSimple(AgentJavaServiceWrapperDiscoveryComponent.PLUGINCONFIG_ENV_FILE);
if (prop != null && prop.getStringValue() != null) {
environmentFile = new File(prop.getStringValue());
}
prop = pc.getSimple(AgentJavaServiceWrapperDiscoveryComponent.PLUGINCONFIG_INC_FILE);
if (prop != null && prop.getStringValue() != null) {
includeFile = new File(prop.getStringValue());
}
return;
}
public void stop() {
// nothing to do
return;
}
public AvailabilityType getAvailability() {
return (launcherScript.exists() && configFile.exists()) ? AvailabilityType.UP : AvailabilityType.DOWN;
// I would like to do this but:
// 1. I don't like executing the script like this every 60 seconds; not very efficient and,
// 2. I don't think executing this script and processing it output will always be faster than
// the 5 seconds the plugin container will give us.
// try {
// String output = executeLauncherScript("status");
// return (output.contains("is installed")) ? AvailabilityType.UP : AvailabilityType.DOWN;
// } catch (Throwable t) {
// return AvailabilityType.DOWN;
// }
}
public OperationResult invokeOperation(String name, Configuration params) throws Exception {
log.info("Wrapper launcher script executing [" + name + "]");
OperationResult result = null;
try {
if (name.equals("Status")) {
Map output = executeLauncherScript("status");
result = new OperationResult();
Integer exitCode = output.keySet().iterator().next();
determineServiceStatus(exitCode, result);
result.getComplexResults().put(new PropertySimple("exitCode", exitCode));
result.getComplexResults().put(new PropertySimple("output", output.values().toArray()[0]));
} else if (name.equals("Install")) {
Map output = executeLauncherScript("install");
result = new OperationResult();
Integer exitCode = output.keySet().iterator().next();
result.getComplexResults().put(new PropertySimple("exitCode", exitCode));
result.getComplexResults().put(new PropertySimple("output", output.values().toArray()[0]));
} else if (name.equals("Restart")) {
executeLauncherScriptInThread("restart");
} else if (name.equals("Stop")) {
executeLauncherScriptInThread("stop");
} else if (name.equals("Remove")) {
executeLauncherScriptInThread("remove");
} else {
throw new UnsupportedOperationException("Invalid operation name: " + name);
}
} catch (Exception e) {
throw new RuntimeException("Failed to invoke operation [" + name + "]", e);
}
log.info("Launcher script executed [" + name + "]: " + result);
return result;
}
private void determineServiceStatus(Integer exitCode, OperationResult result) {
if (exitCode == null || exitCode.intValue() < 0) {
return;
}
int bitmask = exitCode.intValue();
Configuration map = result.getComplexResults();
map.put(new PropertySimple("disabled", ((bitmask & 32) == 32) ? Boolean.TRUE : Boolean.FALSE));
map.put(new PropertySimple("requiresManualStart", ((bitmask & 16) == 16) ? Boolean.TRUE : Boolean.FALSE));
map.put(new PropertySimple("willAutomaticallyStart", ((bitmask & 8) == 8) ? Boolean.TRUE : Boolean.FALSE));
map.put(new PropertySimple("hasInteractiveConsole", ((bitmask & 4) == 4) ? Boolean.TRUE : Boolean.FALSE));
map.put(new PropertySimple("isRunning", ((bitmask & 2) == 2) ? Boolean.TRUE : Boolean.FALSE));
map.put(new PropertySimple("isInstalled", ((bitmask & 1) == 1) ? Boolean.TRUE : Boolean.FALSE));
return;
}
private Map executeLauncherScript(String arg) throws Exception {
if (!this.launcherScript.exists()) {
throw new Exception("Launcher script [" + this.launcherScript + "] does not exist");
}
Map envvars = new HashMap(System.getenv());
envvars.put("RHQ_AGENT_DEBUG", "false"); // we don't want all that debug output in the beginning
ProcessExecution exe = new ProcessExecution(this.launcherScript.getAbsolutePath());
exe.setArguments(new String[] { arg });
exe.setWorkingDirectory(this.launcherScript.getParent());
exe.setCaptureOutput(true);
exe.setWaitForCompletion(30000L);
exe.setEnvironmentVariables(envvars);
ProcessExecutionResults results = this.resourceContext.getSystemInformation().executeProcess(exe);
Throwable error = results.getError();
if (error != null) {
throw new Exception("Failed to invoke [" + this.launcherScript + ' ' + arg + "]", error);
}
HashMap map = new HashMap();
Integer exitCode = results.getExitCode();
String output = results.getCapturedOutput();
map.put((exitCode != null) ? exitCode : Integer.valueOf(-1), (output != null) ? output : "");
return map;
}
/**
* This will execute the launcher script in a separate thread. This separate thread
* will sleep for a few seconds before executing to give the caller enough time to
* return itself. This is used when the launcher script being executed will quickly
* kill the agent VM process in which we are running.
*
* @param arg the command to pass to the launcher script
*
* @throws Exception if failed to even get a chance to spawn the thread and execute the launcher
*/
private void executeLauncherScriptInThread(final String arg) throws Exception {
if (!this.launcherScript.exists()) {
throw new Exception("Launcher script [" + this.launcherScript + "] does not exist");
}
final File script = this.launcherScript;
final SystemInfo sysInfo = this.resourceContext.getSystemInformation();
Thread thread = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(10000L); // this should be enough to return our operation results back
ProcessExecution exe = new ProcessExecution(script.getAbsolutePath());
exe.setArguments(new String[] { arg });
exe.setWorkingDirectory(script.getParent());
ProcessExecutionResults results = sysInfo.executeProcess(exe);
if (results != null && results.getError() != null) {
throw results.getError();
}
} catch (Throwable t) {
log.error("Failed to invoke [" + script + ' ' + arg + "] in a thread", t);
}
}
}, "RHQ Agent Plugin JSW Launcher Thread");
thread.setDaemon(true);
// after we start, do not linger; return fast so we can return our operation results before we die
thread.start();
return;
}
public Configuration loadResourceConfiguration() throws Exception {
Configuration config = new Configuration();
PropertyList conf = loadConfigurationFileConfiguration();
PropertyList env = loadEnvironmentFileConfiguration();
PropertyList inc = loadIncludeFileConfiguration();
if (conf != null) {
config.put(conf);
}
if (env != null) {
config.put(env);
}
if (inc != null) {
config.put(inc);
}
return config;
}
public void updateResourceConfiguration(ConfigurationUpdateReport request) {
try {
updateConfigurationFileConfiguration(request);
updateEnvironmentFileConfiguration(request);
updateIncludeFileConfiguration(request);
} catch (Exception e) {
request.setErrorMessage(new ExceptionPackage(Severity.Severe, e).toString());
}
return;
}
private PropertyList loadConfigurationFileConfiguration() throws Exception {
if (configFile == null || !configFile.exists()) {
return null;
}
// read in the file and get all the settings it defines
EnvironmentScriptFileUpdate updater = EnvironmentScriptFileUpdate.create(configFile.getAbsolutePath());
List properties = updater.loadExisting();
// put the env var definitions in a config object
PropertyList list = new PropertyList("mainConfigurationSettings");
for (NameValuePair prop : properties) {
PropertyMap map = new PropertyMap("mainConfigurationSetting");
map.put(new PropertySimple("name", prop.name));
map.put(new PropertySimple("value", prop.value));
list.add(map);
}
return list;
}
private PropertyList loadEnvironmentFileConfiguration() throws Exception {
if (environmentFile == null || !environmentFile.exists()) {
return null;
}
// read in the file and get all the settings it defines
EnvironmentScriptFileUpdate updater = EnvironmentScriptFileUpdate.create(environmentFile.getAbsolutePath());
List properties = updater.loadExisting();
// put the env var definitions in a config object
PropertyList list = new PropertyList("environmentSettings");
for (NameValuePair prop : properties) {
PropertyMap map = new PropertyMap("environmentSetting");
map.put(new PropertySimple("name", prop.name));
map.put(new PropertySimple("value", prop.value));
list.add(map);
}
return list;
}
private PropertyList loadIncludeFileConfiguration() throws Exception {
if (includeFile == null || !includeFile.exists()) {
return null;
}
// read in the file and get all the settings it defines
EnvironmentScriptFileUpdate updater = EnvironmentScriptFileUpdate.create(includeFile.getAbsolutePath());
List properties = updater.loadExisting();
// put the env var definitions in a config object
PropertyList list = new PropertyList("includeSettings");
for (NameValuePair prop : properties) {
PropertyMap map = new PropertyMap("includeSetting");
map.put(new PropertySimple("name", prop.name));
map.put(new PropertySimple("value", prop.value));
list.add(map);
}
return list;
}
private void updateConfigurationFileConfiguration(ConfigurationUpdateReport request) {
try {
List newSettings = new ArrayList();
Configuration configuration = request.getConfiguration();
PropertyList list = configuration.getList("mainConfigurationSettings");
if (list == null) {
throw new Exception("Missing main config");
}
for (Property item : list.getList()) {
PropertyMap map = (PropertyMap) item;
PropertySimple name = map.getSimple("name");
PropertySimple value = map.getSimple("value");
if (name == null || name.getStringValue() == null) {
log.error("Missing a config name: " + configuration.toString(true));
throw new IllegalArgumentException("Missing the name of a main config setting");
}
if (value != null && value.getStringValue() != null) {
newSettings.add(new NameValuePair(name.getStringValue(), value.getStringValue()));
}
}
// update the env script file so it includes the new settings.
// note that we require the request to contain ALL settings, not a subset; any settings
// missing in the request config that currently exist in the script will be removed from the script,
// which would be bad - but that should never occur unless something bad happens in the UI
EnvironmentScriptFileUpdate updater = EnvironmentScriptFileUpdate.create(configFile.getAbsolutePath());
updater.update(newSettings, true);
request.setStatus(ConfigurationUpdateStatus.SUCCESS);
} catch (Exception e) {
request.setErrorMessage(new ExceptionPackage(Severity.Severe, e).toString());
}
return;
}
private void updateEnvironmentFileConfiguration(ConfigurationUpdateReport request) {
try {
List newSettings = new ArrayList();
Configuration configuration = request.getConfiguration();
PropertyList list = configuration.getList("environmentSettings");
// if there is no config, the file should be deleted
if (list == null || list.getList() == null || list.getList().isEmpty()) {
if (environmentFile.exists()) {
if (!environmentFile.delete()) {
throw new Exception("Failed to remove the env file: " + environmentFile);
}
}
return;
}
for (Property item : list.getList()) {
PropertyMap map = (PropertyMap) item;
PropertySimple name = map.getSimple("name");
PropertySimple value = map.getSimple("value");
if (name == null || name.getStringValue() == null) {
log.error("Missing a env name: " + configuration.toString(true));
throw new IllegalArgumentException("Missing the name of a env setting");
}
if (value != null && value.getStringValue() != null) {
newSettings.add(new NameValuePair(name.getStringValue(), value.getStringValue()));
}
}
// update the env script file so it includes the new settings.
// note that we require the request to contain ALL settings, not a subset; any settings
// missing in the request config that currently exist in the script will be removed from the script,
// which would be bad - but that should never occur unless something bad happens in the UI
EnvironmentScriptFileUpdate updater = EnvironmentScriptFileUpdate.create(environmentFile.getAbsolutePath());
updater.update(newSettings, true);
request.setStatus(ConfigurationUpdateStatus.SUCCESS);
} catch (Exception e) {
request.setErrorMessage(new ExceptionPackage(Severity.Severe, e).toString());
}
return;
}
private void updateIncludeFileConfiguration(ConfigurationUpdateReport request) {
try {
List newSettings = new ArrayList();
Configuration configuration = request.getConfiguration();
PropertyList list = configuration.getList("includeSettings");
// if there is no config, the file should be deleted
if (list == null || list.getList() == null || list.getList().isEmpty()) {
if (includeFile.exists()) {
if (!includeFile.delete()) {
throw new Exception("Failed to remove the include file: " + includeFile);
}
}
return;
}
for (Property item : list.getList()) {
PropertyMap map = (PropertyMap) item;
PropertySimple name = map.getSimple("name");
PropertySimple value = map.getSimple("value");
if (name == null || name.getStringValue() == null) {
log.error("Missing a inc name: " + configuration.toString(true));
throw new IllegalArgumentException("Missing the name of a include setting");
}
if (value != null && value.getStringValue() != null) {
newSettings.add(new NameValuePair(name.getStringValue(), value.getStringValue()));
}
}
// update the env script file so it includes the new settings.
// note that we require the request to contain ALL settings, not a subset; any settings
// missing in the request config that currently exist in the script will be removed from the script,
// which would be bad - but that should never occur unless something bad happens in the UI
EnvironmentScriptFileUpdate updater = EnvironmentScriptFileUpdate.create(includeFile.getAbsolutePath());
updater.update(newSettings, true);
request.setStatus(ConfigurationUpdateStatus.SUCCESS);
} catch (Exception e) {
request.setErrorMessage(new ExceptionPackage(Severity.Severe, e).toString());
}
return;
}
}