/* * RHQ Management Platform * Copyright (C) 2005-2014 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ package org.rhq.plugins.platform; import static org.rhq.core.domain.measurement.AvailabilityType.DOWN; import static org.rhq.core.domain.measurement.AvailabilityType.UP; import static org.rhq.plugins.platform.ProcessComponentConfig.createProcessComponentConfig; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; import java.util.Set; import java.util.StringTokenizer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.rhq.core.domain.configuration.Configuration; import org.rhq.core.domain.measurement.AvailabilityType; import org.rhq.core.domain.measurement.MeasurementDataNumeric; import org.rhq.core.domain.measurement.MeasurementReport; import org.rhq.core.domain.measurement.MeasurementScheduleRequest; 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.measurement.MeasurementFacet; import org.rhq.core.system.AggregateProcessInfo; import org.rhq.core.system.ProcessInfo; import org.rhq.core.system.ProcessInfo.ProcessInfoSnapshot; import org.rhq.core.system.SystemInfo; /** * Monitors a generic process. * * @author Greg Hinkle * @author John Mazzitelli */ public class ProcessComponent implements ResourceComponent, MeasurementFacet { private static final Log LOG = LogFactory.getLog(ProcessComponent.class); private static final String PROCESS_METRIC_PREFIX = "Process."; private ResourceContext resourceContext; private ProcessComponentConfig processComponentConfig; private ProcessInfo process; @Override public void start(ResourceContext resourceContext) throws Exception { this.resourceContext = resourceContext; processComponentConfig = createProcessComponentConfig(resourceContext.getPluginConfiguration()); } @Override public void stop() { resourceContext = null; processComponentConfig = null; process = null; } @Override public AvailabilityType getAvailability() { try { ProcessInfoSnapshot snapshot = getFreshSnapshot(); return (snapshot != null && snapshot.isRunning()) ? UP : DOWN; } catch (Exception e) { if (LOG.isDebugEnabled()) { LOG.debug("Failed to get process info", e); } return DOWN; } } private ProcessInfoSnapshot getFreshSnapshot() throws Exception { ProcessInfoSnapshot snapshot = (process == null) ? null : process.freshSnapshot(); if (snapshot == null || !snapshot.isRunning()) { process = findProcess(processComponentConfig, resourceContext.getSystemInformation()); // Safe to get prior snapshot here, we've just recreated the process info instance snapshot = (process == null) ? null : process.priorSnaphot(); } return snapshot; } static ProcessInfo findProcess(ProcessComponentConfig processComponentConfig, SystemInfo systemInfo) throws Exception { long pid; switch (processComponentConfig.getType()) { case pidFile: pid = getPidFromPidFile(processComponentConfig); break; case piql: pid = getPidFromPiqlExpression(processComponentConfig, systemInfo); break; default: throw new InvalidPluginConfigurationException("Unknown type: " + processComponentConfig.getType()); } if (processComponentConfig.isFullProcessTree()) { return new AggregateProcessInfo(pid); } else { return new ProcessInfo(pid); } } private static long getPidFromPidFile(ProcessComponentConfig processComponentConfig) throws IOException { File file = new File(processComponentConfig.getPidFile()); if (file.canRead()) { FileInputStream fis = new FileInputStream(file); try { BufferedReader r = new BufferedReader(new InputStreamReader(fis)); return Long.parseLong(r.readLine()); } finally { try { fis.close(); } catch (Exception ignore) { } } } else { throw new FileNotFoundException("pidfile [" + processComponentConfig.getPidFile() + "] does not exist or is not allowed to be read. full path=" + file.getAbsolutePath()); } } private static long getPidFromPiqlExpression(ProcessComponentConfig processComponentConfig, SystemInfo systemInfo) throws Exception { List<ProcessInfo> processes = systemInfo.getProcesses(processComponentConfig.getPiql()); if (processes != null && processes.size() == 1) { return processes.get(0).getPid(); } else { throw new Exception("process query [" + processComponentConfig.getPiql() + "] did not return a single process: " + processes); } } @Override public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) { ProcessInfoSnapshot snapshot; try { snapshot = getFreshSnapshot(); if (snapshot == null || !snapshot.isRunning()) { return; } } catch (Exception e) { if (LOG.isDebugEnabled()) { LOG.debug("Failed to get process info", e); } return; } for (MeasurementScheduleRequest request : metrics) { String propertyName = request.getName(); if (!propertyName.startsWith(PROCESS_METRIC_PREFIX)) { continue; } propertyName = propertyName.substring(PROCESS_METRIC_PREFIX.length()); StringTokenizer propertyTokenizer = new StringTokenizer(propertyName, "."); if (!propertyTokenizer.hasMoreTokens()) { continue; } String category = propertyTokenizer.nextToken(); if (!propertyTokenizer.hasMoreTokens()) { continue; } String subcategory = propertyTokenizer.nextToken(); if (category.equals("cpu")) { CpuMetricGatherer cpuMetricGatherer; if (processComponentConfig.isFullProcessTree()) { cpuMetricGatherer = new AggregateProcessCpuMetricGatherer((AggregateProcessInfo) process); } else { cpuMetricGatherer = new ProcessCpuMetricGatherer(snapshot); } addCpuMetric(subcategory, report, request, cpuMetricGatherer); } else if (category.equals("memory")) { MemoryMetricGatherer memoryMetricGatherer; if (processComponentConfig.isFullProcessTree()) { memoryMetricGatherer = new AggregateProcessMemoryMetricGatherer((AggregateProcessInfo) process); } else { memoryMetricGatherer = new ProcessMemoryMetricGatherer(snapshot); } addMemoryMetric(subcategory, report, request, memoryMetricGatherer); } else if (category.equals("fileDescriptor")) { FileDescriptorMetricGatherer fileDescriptorMetricGatherer; if (processComponentConfig.isFullProcessTree()) { fileDescriptorMetricGatherer = new AggregateProcessFileDescriptorMetricGatherer( (AggregateProcessInfo) process); } else { fileDescriptorMetricGatherer = new ProcessFileDescriptorMetricGatherer(snapshot); } addFileDescriptorMetric(subcategory, report, request, fileDescriptorMetricGatherer); } } } private void addCpuMetric(String element, MeasurementReport report, MeasurementScheduleRequest request, CpuMetricGatherer cpuMetricGatherer) { if (element.equals("user")) { report.addData(new MeasurementDataNumeric(request, cpuMetricGatherer.getUser())); } else if (element.equals("sys")) { report.addData(new MeasurementDataNumeric(request, cpuMetricGatherer.getSys())); } else if (element.equals("percent")) { report.addData(new MeasurementDataNumeric(request, cpuMetricGatherer.getPercent())); } } private void addMemoryMetric(String element, MeasurementReport report, MeasurementScheduleRequest request, MemoryMetricGatherer memoryMetricGatherer) { if (element.equals("resident")) { report.addData(new MeasurementDataNumeric(request, memoryMetricGatherer.getResident())); } else if (element.equals("size")) { report.addData(new MeasurementDataNumeric(request, memoryMetricGatherer.getSize())); } } private void addFileDescriptorMetric(String element, MeasurementReport report, MeasurementScheduleRequest request, FileDescriptorMetricGatherer fileDescriptorMetricGatherer) { if (element.equals("total")) { report.addData(new MeasurementDataNumeric(request, fileDescriptorMetricGatherer.getTotal())); } } /** * @deprecated since RHQ4.12. It should not have been exposed. */ @Deprecated protected static ProcessInfo getProcessForConfiguration(Configuration pluginConfig, SystemInfo systemInfo) throws Exception { return findProcess(createProcessComponentConfig(pluginConfig), systemInfo); } private interface CpuMetricGatherer { Double getUser(); Double getSys(); Double getPercent(); } private static class ProcessCpuMetricGatherer implements CpuMetricGatherer { ProcessInfoSnapshot snapshot; ProcessCpuMetricGatherer(ProcessInfoSnapshot snapshot) { this.snapshot = snapshot; } @Override public Double getUser() { return (double) snapshot.getCpu().getUser(); } @Override public Double getSys() { return (double) snapshot.getCpu().getSys(); } @Override public Double getPercent() { return snapshot.getCpu().getPercent(); } } private static class AggregateProcessCpuMetricGatherer implements CpuMetricGatherer { AggregateProcessInfo aggregateProcessInfo; AggregateProcessCpuMetricGatherer(AggregateProcessInfo aggregateProcessInfo) { this.aggregateProcessInfo = aggregateProcessInfo; } @Override public Double getUser() { return (double) aggregateProcessInfo.getAggregateCpu().getUser(); } @Override public Double getSys() { return (double) aggregateProcessInfo.getAggregateCpu().getSys(); } @Override public Double getPercent() { return aggregateProcessInfo.getAggregateCpu().getPercent(); } } private interface MemoryMetricGatherer { Double getResident(); Double getSize(); } private static class ProcessMemoryMetricGatherer implements MemoryMetricGatherer { ProcessInfoSnapshot snapshot; ProcessMemoryMetricGatherer(ProcessInfoSnapshot snapshot) { this.snapshot = snapshot; } @Override public Double getResident() { return (double) snapshot.getMemory().getResident(); } @Override public Double getSize() { return (double) snapshot.getMemory().getSize(); } } private static class AggregateProcessMemoryMetricGatherer implements MemoryMetricGatherer { AggregateProcessInfo aggregateProcessInfo; AggregateProcessMemoryMetricGatherer(AggregateProcessInfo aggregateProcessInfo) { this.aggregateProcessInfo = aggregateProcessInfo; } @Override public Double getResident() { return (double) aggregateProcessInfo.getAggregateMemory().getResident(); } @Override public Double getSize() { return (double) aggregateProcessInfo.getAggregateMemory().getSize(); } } private interface FileDescriptorMetricGatherer { Double getTotal(); } private static class ProcessFileDescriptorMetricGatherer implements FileDescriptorMetricGatherer { ProcessInfoSnapshot snapshot; ProcessFileDescriptorMetricGatherer(ProcessInfoSnapshot snapshot) { this.snapshot = snapshot; } @Override public Double getTotal() { return (double) snapshot.getFileDescriptor().getTotal(); } } private static class AggregateProcessFileDescriptorMetricGatherer implements FileDescriptorMetricGatherer { AggregateProcessInfo aggregateProcessInfo; AggregateProcessFileDescriptorMetricGatherer(AggregateProcessInfo aggregateProcessInfo) { this.aggregateProcessInfo = aggregateProcessInfo; } @Override public Double getTotal() { return (double) aggregateProcessInfo.getAggregateFileDescriptor().getTotal(); } } }