/*
* 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, 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.plugins.platform;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.management.ManagementFactory;
import java.util.List;
import java.util.Set;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.sigar.CpuPerc;
import org.hyperic.sigar.Mem;
import org.hyperic.sigar.Swap;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.ConfigurationUpdateStatus;
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.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.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.util.ObjectUtil;
import org.rhq.core.system.ProcessInfo;
import org.rhq.core.system.SigarAccess;
import org.rhq.core.system.SystemInfo;
/**
* Represents the platform resource which is the root resource for all other resources managed.
*
* @author John Mazzitelli
*/
public class PlatformComponent implements ResourceComponent<PlatformComponent>, ConfigurationFacet, MeasurementFacet,
OperationFacet {
private final Log log = LogFactory.getLog(PlatformComponent.class);
/**
* This is a substring that starts all native-only measurement property names. If a measurement property name starts
* with this, it can only be collected if we have native support for our platform.
*/
private static final String NATIVE_INDICATOR = "Native.";
/**
* This is a substring that starts all trait measurement property names. If its a native trait, it will appear after
* the {@link #NATIVE_INDICATOR}.
*/
private static final String TRAIT_INDICATOR = "Trait.";
// these are the "property" names for all trait metrics
protected static final String TRAIT_HOSTNAME = TRAIT_INDICATOR + "hostname";
protected static final String TRAIT_OSNAME = TRAIT_INDICATOR + "osname";
protected static final String TRAIT_OSVERSION = TRAIT_INDICATOR + "osversion";
protected static final String TRAIT_ARCH = TRAIT_INDICATOR + "sysarch";
protected ResourceContext resourceContext;
private SystemInfo sysinfo;
public void start(ResourceContext context) {
this.resourceContext = context;
this.sysinfo = context.getSystemInformation();
}
public void stop() {
}
public AvailabilityType getAvailability() {
// if we are running then obviously, by definition, the platform is up
return AvailabilityType.UP;
}
public Configuration loadResourceConfiguration() {
// platform configuration is "read-only" and is exposed as traits
Configuration config = new Configuration();
return config;
}
public void updateResourceConfiguration(ConfigurationUpdateReport report) {
// most of the platform configuration are "read-only" and
// are implemented as traits - nothing to update
report.setErrorMessage("Cannot update platform resource configuration - it is read only");
report.setStatus(ConfigurationUpdateStatus.FAILURE);
return;
}
public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) {
SystemInfo info = this.resourceContext.getSystemInformation();
boolean isNative = info.isNative();
Mem platformMemoryInfo = null;
Swap platformSwapInfo = null;
CpuPerc cpuPerc = null;
try {
cpuPerc = SigarAccess.getSigar().getCpuPerc();
} catch (Exception e) {
// probably native api is unavailable, but getCpuPerc might also have a problem; in either case, nothing we can do
}
for (MeasurementScheduleRequest request : metrics) {
String property = request.getName();
if (property.startsWith(NATIVE_INDICATOR)) {
// we cannot collect a native measurement without native support - go on to the next
if (!isNative) {
continue;
}
property = property.substring(NATIVE_INDICATOR.length());
}
if (property.startsWith(TRAIT_INDICATOR)) {
report.addData(getMeasurementDataTrait(request));
} else if (property.startsWith("MemoryInfo.")) {
if (platformMemoryInfo == null) {
platformMemoryInfo = info.getMemoryInfo();
}
property = property.substring(property.indexOf(".") + 1);
double memoryValue = ((Number) getObjectProperty(platformMemoryInfo, property)).doubleValue();
report.addData(new MeasurementDataNumeric(request, memoryValue));
} else if (property.startsWith("SwapInfo.")) {
if (platformSwapInfo == null) {
platformSwapInfo = info.getSwapInfo();
}
if (platformSwapInfo != null) {
property = property.substring(property.indexOf(".") + 1);
double swapValue = ((Number) getObjectProperty(platformSwapInfo, property)).doubleValue();
report.addData(new MeasurementDataNumeric(request, swapValue));
}
} else if (property.startsWith("CpuPerc.")) {
if (cpuPerc != null) {
property = property.substring(property.indexOf(".") + 1);
Number num = ((Number) ObjectUtil.lookupAttributeProperty(cpuPerc, property));
if (num != null) {
report.addData(new MeasurementDataNumeric(request, num.doubleValue()));
}
}
}
}
}
private Object getObjectProperty(Object object, String name) {
try {
BeanInfo info = Introspector.getBeanInfo(object.getClass());
for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
if (pd.getName().equals(name)) {
return pd.getReadMethod().invoke(object);
}
}
} catch (Exception skip) {
if (log.isDebugEnabled())
log.debug(skip);
}
return Double.NaN;
}
protected MeasurementDataTrait getMeasurementDataTrait(MeasurementScheduleRequest request) {
String name = request.getName();
MeasurementDataTrait trait = new MeasurementDataTrait(request, "?");
try {
if (TRAIT_HOSTNAME.indexOf(name) > -1) {
trait.setValue(sysinfo.getHostname());
} else if (TRAIT_OSNAME.indexOf(name) > -1) {
trait.setValue(sysinfo.getOperatingSystemName());
} else if (TRAIT_OSVERSION.indexOf(name) > -1) {
trait.setValue(sysinfo.getOperatingSystemVersion());
} else if (TRAIT_ARCH.indexOf(name) > -1) {
trait.setValue(sysinfo.getSystemArchitecture());
} else {
log.error("Being asked to collect an unknown trait measurement: " + name);
}
} catch (Exception skip) {
log.debug(skip);
}
return trait;
}
public OperationResult invokeOperation(String name, Configuration parameters) throws Exception {
if ("discovery".equals(name)) {
Boolean detailed = parameters.getSimple("detailedDiscovery").getBooleanValue();
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
String results = (String) mbs.invoke(new ObjectName("rhq.pc:type=PluginContainer"), "executeDiscovery",
new Object[] { detailed }, new String[] { Boolean.class.getName() });
return new OperationResult(results);
} else if ("viewProcessList".equals(name)) {
OperationResult result = new OperationResult();
List<ProcessInfo> processes = this.resourceContext.getSystemInformation().getAllProcesses();
PropertyList processList = new PropertyList("processList");
for (ProcessInfo process : processes) {
PropertyMap pm = new PropertyMap("process");
pm.put(new PropertySimple("pid", process.getPid()));
pm.put(new PropertySimple("name", process.getBaseName()));
pm.put(new PropertySimple("size", (process.getMemory() != null) ? process.getMemory().getSize() : "0"));
pm.put(new PropertySimple("userTime", (process.getTime() != null) ? process.getTime().getUser() : "0"));
pm.put(new PropertySimple("kernelTime", (process.getTime() != null) ? process.getTime().getSys() : "0"));
processList.add(pm);
}
result.getComplexResults().put(processList);
return result;
}
throw new UnsupportedOperationException("Operation [" + name + "] not supported on "
+ resourceContext.getResourceType() + ".");
}
public SystemInfo getSysinfo() {
return sysinfo;
}
}