/* * 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.BufferedInputStream; import java.io.CharArrayWriter; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.ExecutionException; import javax.management.MBeanException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.mc4j.ems.connection.EmsInvocationException; import org.mc4j.ems.connection.bean.EmsBean; import org.rhq.core.domain.configuration.Configuration; import org.rhq.core.domain.configuration.ConfigurationUpdateStatus; import org.rhq.core.domain.configuration.PropertySimple; import org.rhq.core.domain.measurement.AvailabilityType; import org.rhq.core.domain.measurement.MeasurementDataNumeric; import org.rhq.core.domain.measurement.MeasurementDataTrait; import org.rhq.core.domain.measurement.MeasurementReport; import org.rhq.core.domain.measurement.MeasurementScheduleRequest; import org.rhq.core.pluginapi.configuration.ConfigurationFacet; import org.rhq.core.pluginapi.configuration.ConfigurationUpdateReport; import org.rhq.core.pluginapi.event.log.LogFileEventResourceComponentHelper; import org.rhq.core.pluginapi.inventory.ResourceComponent; import org.rhq.core.pluginapi.inventory.ResourceContext; import org.rhq.core.pluginapi.measurement.MeasurementFacet; import org.rhq.core.pluginapi.operation.OperationFacet; import org.rhq.core.pluginapi.operation.OperationResult; import org.rhq.core.pluginapi.support.SnapshotReportRequest; import org.rhq.core.pluginapi.support.SnapshotReportResults; import org.rhq.core.pluginapi.support.SupportFacet; import org.rhq.core.system.SystemInfoFactory; import org.rhq.core.util.exception.ExceptionPackage; import org.rhq.core.util.exception.Severity; import org.rhq.enterprise.agent.AgentManagement; import org.rhq.enterprise.agent.AgentManagementMBean; import org.rhq.plugins.jmx.JMXComponent; import org.rhq.plugins.jmx.JMXServerComponent; /** * The component that represents the Agent itself. * * @author John Mazzitelli */ public class AgentServerComponent<T extends ResourceComponent<?>> extends JMXServerComponent<T> implements JMXComponent<T>, MeasurementFacet, OperationFacet, ConfigurationFacet, SupportFacet { private static Log log = LogFactory.getLog(AgentServerComponent.class); /** * This is a substring that starts all trait measurement property names. */ private static final String TRAIT_INDICATOR = "Trait."; private static final String SIGAR_VERSION_METRIC_SUFFIX = "SigarVersion"; private static final String REASON_FOR_LAST_RESTART_METRIC_SUFFIX = "ReasonForLastRestart"; private static final String AGENT_HOME_DIRECTORY_METRIC_SUFFIX = "AgentHomeDirectory"; private String sigarVersion; // needed for log tracking private LogFileEventResourceComponentHelper logFileEventDelegate; @Override public void start(ResourceContext resourceContext) throws Exception { super.start(resourceContext); this.sigarVersion = SystemInfoFactory.getNativeSystemInfoVersion(); this.logFileEventDelegate = new LogFileEventResourceComponentHelper(resourceContext); this.logFileEventDelegate.startLogFileEventPollers(); } @Override public void stop() { this.logFileEventDelegate.stopLogFileEventPollers(); super.stop(); } @Override public AvailabilityType getAvailability() { // this one is simple - if this plugin is running, then we automatically know the agent is up! return AvailabilityType.UP; } protected EmsBean getAgentBean() { return getEmsConnection().getBean(AgentManagement.singletonObjectName.getCanonicalName()); } public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) { for (MeasurementScheduleRequest metric_request : metrics) { String metric_name = metric_request.getName(); if (metric_name.startsWith(TRAIT_INDICATOR)) { try { if (metric_name.endsWith(SIGAR_VERSION_METRIC_SUFFIX)) { report.addData(new MeasurementDataTrait(metric_request, this.sigarVersion)); } else if (metric_name.endsWith(REASON_FOR_LAST_RESTART_METRIC_SUFFIX)) { Object reason = getAgentBean().getAttribute(REASON_FOR_LAST_RESTART_METRIC_SUFFIX).refresh(); report.addData(new MeasurementDataTrait(metric_request, reason.toString())); } else if (metric_name.endsWith(AGENT_HOME_DIRECTORY_METRIC_SUFFIX)) { Object reason = getAgentBean().getAttribute(AGENT_HOME_DIRECTORY_METRIC_SUFFIX).refresh(); report.addData(new MeasurementDataTrait(metric_request, reason.toString())); } else { log.error("Being asked to collect an unknown trait measurement: " + metric_name); } } catch (Exception skip) { log.debug(skip); } } else { // I know that all current metrics are either long or int - so they can be cast to Number Number metric_value; try { metric_value = (Number) getAgentBean().getAttribute(metric_name).refresh(); // This is always local, just update individual metrics report.addData(new MeasurementDataNumeric(metric_request, metric_value.doubleValue())); } catch (Exception e) { log.error("Failed to obtain metric [" + metric_name + "]. Cause: " + e); } } } return; } public OperationResult invokeOperation(String name, Configuration params) { OperationResult result = null; // I know all operation names have identical MBean operations on the agent management MBean // I also know about all operations that have void and non-void parameters try { if ((params == null) || (params.getProperties().size() == 0)) { result = (OperationResult) getAgentBean().getOperation(name).invoke(); } else { if (name.equals("retrievePluginInfo")) { String pluginToUpdate = params.getSimple("pluginName").getStringValue(); result = (OperationResult) getAgentBean().getOperation(name).invoke(pluginToUpdate); } else if (name.equals("executeAvailabilityScan")) { Boolean changesOnly = params.getSimple("changesOnly").getBooleanValue(); result = (OperationResult) getAgentBean().getOperation(name).invoke(changesOnly); } else if (name.equals("retrieveCurrentDateTime")) { String timeZone = params.getSimple("timeZone").getStringValue(); result = new OperationResult(); result.getComplexResults().put( new PropertySimple("dateTime", getAgentBean().getOperation(name).invoke(timeZone))); } else if (name.equals("setDebugMode")) { Boolean enabled = params.getSimple("enabled").getBooleanValue(); Boolean traceMessaging = params.getSimple("traceMessaging").getBooleanValue(); result = new OperationResult(); getAgentBean().getOperation(name).invoke(enabled, traceMessaging); } else if (name.equals("executePromptCommand")) { String command = params.getSimple("command").getStringValue(); result = new OperationResult(); try { Object output = getAgentBean().getOperation(name).invoke(command); result.getComplexResults().put(new PropertySimple("output", output)); } catch (EmsInvocationException eie) { if (eie.getCause() instanceof MBeanException && eie.getCause().getCause() instanceof ExecutionException) { // the prompt command threw the exception - in this case: // the message is the prompt output and the cause is the actual prompt exception ExecutionException ee = (ExecutionException) eie.getCause().getCause(); String output = ee.getMessage(); CharArrayWriter caw = new CharArrayWriter(); ee.getCause().printStackTrace(new PrintWriter(caw)); String error = caw.toString(); result.getComplexResults().put(new PropertySimple("output", output)); result.getComplexResults().put(new PropertySimple("error", error)); } else { throw eie; } } } else if (name.equals("switchToServer")) { String server = params.getSimpleValue("server", null); getAgentBean().getOperation(name).invoke(server); } else { // this should really never happen throw new IllegalArgumentException("Operation [" + name + "] does not support params"); } } } catch (Exception e) { throw new RuntimeException("Failed to invoke operation [" + name + "]", e); } return result; } public Configuration loadResourceConfiguration() { // the agent preferences are simple name/value pairs; just create a Configuration // with a bunch of simple properties, one simple property for each agent preference AgentManagementMBean mbean = AgentDiscoveryComponent.getAgentManagementMBean(); Properties agent_prefs = mbean.getAgentConfiguration(); Configuration config = new Configuration(); for (Map.Entry<Object, Object> pref : agent_prefs.entrySet()) { config.put(new PropertySimple(pref.getKey().toString(), pref.getValue())); } return config; } public void updateResourceConfiguration(ConfigurationUpdateReport request) { Configuration configuration = request.getConfiguration(); Collection<String> config_names = configuration.getNames(); Properties prefsToMerge = new Properties(); List<String> prefsToRemove = new ArrayList<String>(); for (String config_name : config_names) { String config_value = configuration.getSimple(config_name).getStringValue(); // skip props w/ null values, since java.util.Properties does not allow null values. if (config_value != null) { prefsToMerge.setProperty(config_name, config_value); } else { prefsToRemove.add(config_name); } } try { AgentManagementMBean mbean = AgentDiscoveryComponent.getAgentManagementMBean(); mbean.mergeIntoAgentConfiguration(prefsToMerge); mbean.removeFromAgentConfiguration(prefsToRemove); request.setStatus(ConfigurationUpdateStatus.SUCCESS); } catch (Exception e) { request.setErrorMessage(new ExceptionPackage(Severity.Severe, e).toString()); } return; } public SnapshotReportResults getSnapshotReport(SnapshotReportRequest request) throws Exception { AgentManagementMBean mbean = AgentDiscoveryComponent.getAgentManagementMBean(); ResourceContext resourceContext = getResourceContext(); Configuration pluginConfig = resourceContext.getPluginConfiguration(); String installDir = mbean.getAgentHomeDirectory(); Properties config = mbean.getAgentConfiguration(); String tmpDir = resourceContext.getTemporaryDirectory().getAbsolutePath(); String name = request.getName(); String description = request.getDescription(); AgentSnapshotReport report = new AgentSnapshotReport(name, description, pluginConfig, installDir, config, tmpDir); File reportFile = report.generate(); InputStream inputStream = new BufferedInputStream(new FileInputStream(reportFile)); SnapshotReportResults results = new SnapshotReportResults(inputStream); return results; } }