/*
* RHQ Management Platform
* Copyright (C) 2005-2013 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.apache;
import static org.rhq.core.domain.measurement.AvailabilityType.DOWN;
import static org.rhq.core.domain.measurement.AvailabilityType.UP;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.File;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.augeas.AugeasComponent;
import org.rhq.augeas.node.AugeasNode;
import org.rhq.augeas.tree.AugeasTree;
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.configuration.definition.ConfigurationDefinition;
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.measurement.DataType;
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.domain.measurement.calltime.CallTimeData;
import org.rhq.core.domain.resource.CreateResourceStatus;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.pluginapi.configuration.ConfigurationFacet;
import org.rhq.core.pluginapi.configuration.ConfigurationUpdateReport;
import org.rhq.core.pluginapi.inventory.CreateChildResourceFacet;
import org.rhq.core.pluginapi.inventory.CreateResourceReport;
import org.rhq.core.pluginapi.inventory.DeleteResourceFacet;
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.pluginapi.util.ResponseTimeConfiguration;
import org.rhq.core.pluginapi.util.ResponseTimeLogParser;
import org.rhq.plugins.apache.mapping.ApacheAugeasMapping;
import org.rhq.plugins.apache.parser.ApacheDirective;
import org.rhq.plugins.apache.parser.ApacheDirectiveTree;
import org.rhq.plugins.apache.util.AugeasNodeSearch;
import org.rhq.plugins.apache.util.AugeasNodeValueUtil;
import org.rhq.plugins.apache.util.ConfigurationTimestamp;
import org.rhq.plugins.apache.util.PluginUtility;
import org.rhq.plugins.apache.util.RuntimeApacheConfiguration;
import org.rhq.plugins.www.snmp.SNMPException;
import org.rhq.plugins.www.snmp.SNMPSession;
import org.rhq.plugins.www.snmp.SNMPValue;
import org.rhq.plugins.www.util.WWWUtils;
/**
* @author Ian Springer
* @author Lukas Krejci
*/
public class ApacheVirtualHostServiceComponent implements ResourceComponent<ApacheServerComponent>, MeasurementFacet,
ConfigurationFacet, DeleteResourceFacet, CreateChildResourceFacet {
private static final Log LOG = LogFactory.getLog(ApacheVirtualHostServiceComponent.class);
public static final String URL_CONFIG_PROP = "url";
public static final String MAIN_SERVER_RESOURCE_KEY = "MainServer";
public static final String RESPONSE_TIME_LOG_FILE_CONFIG_PROP =
ResponseTimeConfiguration.RESPONSE_TIME_LOG_FILE_CONFIG_PROP;
public static final String RESPONSE_TIME_URL_EXCLUDES_CONFIG_PROP =
ResponseTimeConfiguration.RESPONSE_TIME_URL_EXCLUDES_CONFIG_PROP;
public static final String RESPONSE_TIME_URL_TRANSFORMS_CONFIG_PROP =
ResponseTimeConfiguration.RESPONSE_TIME_URL_TRANSFORMS_CONFIG_PROP;
public static final String SERVER_NAME_CONFIG_PROP = "ServerName";
private static final String RESPONSE_TIME_METRIC = "ResponseTime";
/** Multiply by 1/1000 to convert logged response times, which are in microseconds, to milliseconds. */
private static final double RESPONSE_TIME_LOG_TIME_MULTIPLIER = 0.001;
public static final String RESOURCE_TYPE_NAME = "Apache Virtual Host";
private ResourceContext<ApacheServerComponent> resourceContext;
private URL url;
private ResponseTimeLogParser logParser;
private ConfigurationTimestamp lastConfigurationTimeStamp = new ConfigurationTimestamp();
private int snmpWwwServiceIndex = -1;
private AvailabilityType lastKnownAvailability;
public void start(ResourceContext<ApacheServerComponent> resourceContext) throws Exception {
this.resourceContext = resourceContext;
Configuration pluginConfig = this.resourceContext.getPluginConfiguration();
String url = pluginConfig.getSimple(URL_CONFIG_PROP).getStringValue();
if (url != null) {
try {
this.url = new URL(url);
if (this.url.getPort() == 0) {
throw new InvalidPluginConfigurationException(
"The 'url' connection property is invalid - 0 is not a valid port; please change the value to the "
+ "port this virtual host is listening on. NOTE: If the 'url' property was set this way "
+ "after autodiscovery, you most likely did not include the port in the ServerName directive for "
+ "this virtual host in httpd.conf.");
}
} catch (MalformedURLException e) {
throw new Exception("Value of '" + URL_CONFIG_PROP + "' connection property ('" + url
+ "') is not a valid URL.");
}
}
ResponseTimeConfiguration responseTimeConfig = new ResponseTimeConfiguration(pluginConfig);
File logFile = responseTimeConfig.getLogFile();
if (logFile != null) {
this.logParser = new ResponseTimeLogParser(logFile, RESPONSE_TIME_LOG_TIME_MULTIPLIER);
this.logParser.setExcludes(responseTimeConfig.getExcludes());
this.logParser.setTransforms(responseTimeConfig.getTransforms());
}
this.lastKnownAvailability = UP;
}
public void stop() {
this.resourceContext = null;
this.url = null;
this.lastKnownAvailability = null;
}
public AvailabilityType getAvailability() {
if (resourceContext.getParentResourceComponent().getUseBMX())
lastKnownAvailability = getAvailabilityBMXInternal();
else
lastKnownAvailability = getAvailabilityInternal();
return lastKnownAvailability;
}
public AvailabilityType getAvailabilityBMXInternal() {
boolean availability = false;
HttpURLConnection urlConn = null;
try {
URL url = new URL(resourceContext.getParentResourceComponent().getBMXUrl());
urlConn = (HttpURLConnection) url.openConnection();
urlConn.connect();
if(urlConn.getResponseCode() == HttpURLConnection.HTTP_OK)
availability = true;
else
availability = false;
} catch (ConnectException e) {
LOG.info("The Apache server is down !");
} catch (MalformedURLException e) {
LOG.error("Malformed URL : " + resourceContext.getParentResourceComponent().getBMXUrl());
} catch (IOException e) {
LOG.error("IO Error on : " + resourceContext.getParentResourceComponent().getBMXUrl());
} finally {
if (urlConn != null) {
try {
urlConn.disconnect();
} catch (Exception e) {
// Ignored it.
}
}
}
if(!availability)
return AvailabilityType.DOWN;
else
return AvailabilityType.UP;
}
private AvailabilityType getAvailabilityInternal() {
if (url != null) {
int timeout = PluginUtility.getAvailabilityFacetTimeout();
AvailabilityResult availabilityResult = WWWUtils.checkAvailability(this.url, timeout);
if (availabilityResult.getAvailabilityType() == UP) {
return UP;
} else {
if (lastKnownAvailability == UP) {
switch (availabilityResult.getErrorType()) {
case CANNOT_CONNECT:
LOG.warn("Could not connect to Virtual Host " + resourceContext.getResourceDetails()
+ ", availability will be reported as " + DOWN.name());
break;
case CONNECTION_TIMEOUT:
LOG.warn("Connection to Virtual Host " + resourceContext.getResourceDetails()
+ " timed out, availability will be reported as " + DOWN.name());
break;
default:
}
}
return DOWN;
}
} else {
try {
//we don't need the SNMP connection to figure out the index on which the SNMP
//module would report this vhost. So first, let's check if that index is valid
//(i.e. check that the vhost is actually still present in the apache configuration)
if (getWwwServiceIndex() < 1) {
return DOWN;
}
//ok, so the vhost is present. Now let's just ping the SNMP module to see
//if it is reachable and base our availability on that...
SNMPSession snmpSession = resourceContext.getParentResourceComponent().getSNMPSession();
return snmpSession.ping() ? UP : DOWN;
} catch (Exception e) {
LOG.debug("Determining the availability of the vhost [" + resourceContext.getResourceKey()
+ "] using SNMP failed.", e);
return DOWN;
}
}
}
public Configuration loadResourceConfiguration() throws Exception {
// BZ 858813 - treat Augeas disabled as configuration disabled and just return null, otherwise
// we spam the log.
if (!isAugeasEnabled()) {
LOG.debug(ApacheServerComponent.CONFIGURATION_NOT_SUPPORTED_ERROR_MESSAGE);
return null;
}
ApacheServerComponent parent = resourceContext.getParentResourceComponent();
AugeasComponent comp = getAugeas();
try {
AugeasTree tree = comp.getAugeasTree(ApacheServerComponent.AUGEAS_HTTP_MODULE_NAME);
ConfigurationDefinition resourceConfigDef =
resourceContext.getResourceType().getResourceConfigurationDefinition();
ApacheAugeasMapping mapping = new ApacheAugeasMapping(tree);
return mapping.updateConfiguration(getNode(tree), resourceConfigDef);
} finally {
comp.close();
}
}
public void updateResourceConfiguration(ConfigurationUpdateReport report) {
if (!isAugeasEnabled()) {
report.setStatus(ConfigurationUpdateStatus.FAILURE);
report.setErrorMessage(ApacheServerComponent.CONFIGURATION_NOT_SUPPORTED_ERROR_MESSAGE);
return;
}
AugeasComponent comp = getAugeas();
AugeasTree tree = null;
try {
tree = comp.getAugeasTree(ApacheServerComponent.AUGEAS_HTTP_MODULE_NAME);
ConfigurationDefinition resourceConfigDef =
resourceContext.getResourceType().getResourceConfigurationDefinition();
ApacheAugeasMapping mapping = new ApacheAugeasMapping(tree);
AugeasNode virtHostNode = getNode(tree);
mapping.updateAugeas(virtHostNode, report.getConfiguration(), resourceConfigDef);
tree.save();
report.setStatus(ConfigurationUpdateStatus.SUCCESS);
LOG.info("Apache configuration was updated");
finishConfigurationUpdate(report);
} catch (Exception e) {
if (tree != null) {
String message = "Augeas failed to save configuration " + tree.summarizeAugeasError();
report.setErrorMessage(message);
LOG.error(message);
} else {
report.setErrorMessageFromThrowable(e);
LOG.error("Augeas failed to save configuration", e);
}
report.setStatus(ConfigurationUpdateStatus.FAILURE);
} finally {
comp.close();
}
}
public void deleteResource() throws Exception {
if (!isAugeasEnabled()) {
throw new IllegalStateException(ApacheServerComponent.CONFIGURATION_NOT_SUPPORTED_ERROR_MESSAGE);
}
ApacheServerComponent parent = resourceContext.getParentResourceComponent();
if (MAIN_SERVER_RESOURCE_KEY.equals(resourceContext.getResourceKey())) {
throw new IllegalArgumentException(
"Cannot delete the virtual host representing the main server configuration.");
}
AugeasComponent comp = getAugeas();
try {
AugeasTree tree = comp.getAugeasTree(ApacheServerComponent.AUGEAS_HTTP_MODULE_NAME);
AugeasNode myNode = getNode(tree);
tree.removeNode(myNode, true);
tree.save();
deleteEmptyFile(tree, myNode);
conditionalRestart();
} catch (IllegalStateException e) {
//this means we couldn't find the augeas node for this vhost.
//that error can be safely ignored in this situation.
} finally {
comp.close();
}
}
public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> schedules) throws Exception {
if (resourceContext.getParentResourceComponent().getUseBMX())
getBMXValues(report, schedules);
else
getSNMPValues(report, schedules);
}
private void getSNMPValues(MeasurementReport report, Set<MeasurementScheduleRequest> schedules) throws Exception {
SNMPSession snmpSession = this.resourceContext.getParentResourceComponent().getSNMPSession();
int primaryIndex = getWwwServiceIndex();
boolean ping = snmpSession.ping();
boolean snmpMetricsSupported = primaryIndex >= 0 && ping;
if (LOG.isDebugEnabled()) {
if (snmpMetricsSupported) {
LOG.debug("SNMP metrics collection supported for VirtualHost service #" + primaryIndex);
} else {
LOG.debug("SNMP metrics collection unsupported for VirtualHost: primaryIndex[" + primaryIndex
+ "], session[" + snmpSession + "], ping[" + ping + "]");
}
}
for (MeasurementScheduleRequest schedule : schedules) {
String metricName = schedule.getName();
if (metricName.equals(RESPONSE_TIME_METRIC)) {
if (this.logParser != null) {
try {
CallTimeData callTimeData = new CallTimeData(schedule);
this.logParser.parseLog(callTimeData);
report.addData(callTimeData);
} catch (Exception e) {
LOG.error("Failed to retrieve HTTP call-time data.", e);
}
} else {
LOG.error("The '" + RESPONSE_TIME_METRIC + "' metric is enabled for resource '"
+ this.resourceContext.getResourceKey() + "', but no value is defined for the '"
+ RESPONSE_TIME_LOG_FILE_CONFIG_PROP + "' connection property.");
// TODO: Communicate this error back to the server for display in the GUI.
}
} else {
if (snmpMetricsSupported) {
// Assume anything else is an SNMP metric.
try {
collectSnmpMetric(report, primaryIndex, snmpSession, schedule);
} catch (SNMPException e) {
LOG.error("An error occurred while attempting to collect an SNMP metric.", e);
}
}
}
}
LOG.info("Collected " + report.getDataCount() + " metrics for VirtualHost "
+ this.resourceContext.getResourceKey() + ".");
}
private void getBMXValues(MeasurementReport report, Set<MeasurementScheduleRequest> schedules) throws Exception {
Map<String,String> values = ApacheServerComponent.parseBMXInput(this.resourceContext.getResourceKey(), resourceContext.getParentResourceComponent().getBMXUrl());
if (LOG.isDebugEnabled()) {
LOG.debug("Collecting BMX metrics");
}
for (MeasurementScheduleRequest schedule : schedules) {
/* convert the name like mod_snmp */
String metricName = ApacheServerComponent.convertStringToBMX(schedule.getName());
if (LOG.isDebugEnabled()) {
LOG.debug("Collecting BMX metric [" + metricName + "]");
}
if (values.containsKey(metricName)) {
if (schedule.getDataType()== DataType.TRAIT) {
String val = values.get(metricName);
if (LOG.isDebugEnabled()) {
LOG.debug("Collected BMX metric [" + metricName + "], value = " + val);
}
MeasurementDataTrait mdt = new MeasurementDataTrait(schedule,val);
report.addData(mdt);
} else {
Double val = Double.valueOf(values.get(metricName));
if (LOG.isDebugEnabled()) {
LOG.debug("Collected BMX metric [" + metricName + "], value = " + val);
}
MeasurementDataNumeric mdn = new MeasurementDataNumeric(schedule,val);
report.addData(mdn);
}
} else if (metricName.equals(RESPONSE_TIME_METRIC)) {
/* a special handling for ResponseTime */
if (this.logParser != null) {
try {
CallTimeData callTimeData = new CallTimeData(schedule);
this.logParser.parseLog(callTimeData);
report.addData(callTimeData);
} catch (Exception e) {
LOG.error("Failed to retrieve HTTP call-time data.", e);
}
} else {
LOG.error("The '" + RESPONSE_TIME_METRIC + "' metric is enabled for resource '"
+ this.resourceContext.getResourceKey() + "', but no value is defined for the '"
+ RESPONSE_TIME_LOG_FILE_CONFIG_PROP + "' connection property.");
// TODO: Communicate this error back to the server for display in the GUI.
}
} else {
LOG.warn("BMX metric [" + metricName + "] not found");
}
}
LOG.info("Collected " + report.getDataCount() + " metrics for VirtualHost "
+ this.resourceContext.getResourceKey() + ".");
}
public CreateResourceReport createResource(CreateResourceReport report) {
if (!isAugeasEnabled()) {
report.setStatus(CreateResourceStatus.FAILURE);
report.setErrorMessage(ApacheServerComponent.CONFIGURATION_NOT_SUPPORTED_ERROR_MESSAGE);
return report;
}
ResourceType resourceType = report.getResourceType();
AugeasComponent comp = null;
try {
comp = getAugeas();
if (resourceType.equals(getDirectoryResourceType())) {
Configuration resourceConfiguration = report.getResourceConfiguration();
Configuration pluginConfiguration = report.getPluginConfiguration();
String directoryName = report.getUserSpecifiedResourceName();
//fill in the plugin configuration
//get the directive index
AugeasTree tree = comp.getAugeasTree(ApacheServerComponent.AUGEAS_HTTP_MODULE_NAME);
AugeasNode myNode = getNode(tree);
List<AugeasNode> directories = myNode.getChildByLabel("<Directory");
int seq = 1;
/*
* myNode will be parent node of the new Directory node.
* We need to create a new node for directory node which will contain child nodes.
* To create a node we can call method from AugeasTree which will create a node. In this method is
* parameter sequence, if we will leave this parameter empty and there will be more nodes with
* the same label, new node will be created but the method createNode will return node with index 0 resp 1.
* If that will happen we can not update the node anymore because we are updating wrong node.
* To avoid this situation we need to know what is the last sequence nr. of virtual host's child (directory) nodes.
* We can not just count child nodes with the same label because some of the child nodes
* could be stored in another file. So that in httpd configurationstructure they are child nodes of virtual host,
* but in augeas configuration structure they can be child nodes of node Include[];.
*/
for (AugeasNode n : directories) {
String param = n.getFullPath();
int end = param.lastIndexOf(File.separatorChar);
if (end != -1)
if (myNode.getFullPath().equals(param.substring(0, end)))
seq++;
}
//pluginConfiguration.put(new PropertySimple(ApacheDirectoryComponent.DIRECTIVE_INDEX_PROP, seq));
//we don't support this yet... need to figure out how...
pluginConfiguration.put(new PropertySimple(ApacheDirectoryComponent.REGEXP_PROP, false));
String dirNameToSet = AugeasNodeValueUtil.escape(directoryName);
//now actually create the data in augeas
try {
ApacheAugeasMapping mapping = new ApacheAugeasMapping(tree);
AugeasNode directoryNode = tree.createNode(myNode, "<Directory", null, seq);
String myNodeKey = AugeasNodeSearch.getNodeKey(myNode, tree.getRootNode());
tree.createNode(directoryNode, "param", dirNameToSet, 0);
mapping.updateAugeas(directoryNode, resourceConfiguration,
resourceType.getResourceConfigurationDefinition());
tree.save();
comp.close();
tree = comp.getAugeasTree(ApacheServerComponent.AUGEAS_HTTP_MODULE_NAME);
AugeasNode parentNode;
if (myNodeKey.equals("")) {
parentNode = tree.getRootNode();
} else
parentNode = AugeasNodeSearch.findNodeById(tree.getRootNode(), myNodeKey);
List<AugeasNode> nodes = parentNode.getChildByLabel("<Directory");
if (nodes.size() < seq) {
report.setStatus(CreateResourceStatus.FAILURE);
report.setErrorMessage("Could not create directory node.");
}
AugeasNode nd = nodes.get(seq - 1);
String key = AugeasNodeSearch.getNodeKey(nd, parentNode);
report.setResourceKey(key);
report.setResourceName(directoryName);
report.setStatus(CreateResourceStatus.SUCCESS);
resourceContext.getParentResourceComponent().finishChildResourceCreate(report);
} catch (Exception e) {
LOG.error("Could not create httpd virtual host child resource.", e);
report.setException(e);
report.setStatus(CreateResourceStatus.FAILURE);
}
} else {
report.setErrorMessage("Unable to create resources of type " + resourceType.getName());
report.setStatus(CreateResourceStatus.FAILURE);
}
} finally {
if (comp != null)
comp.close();
}
return report;
}
public AugeasComponent getAugeas() {
return resourceContext.getParentResourceComponent().getAugeas();
}
/**
* Returns a node corresponding to this component in the Augeas tree.
*
* @param tree
* @return
* @throws IllegalStateException if none or more than one nodes found
*/
public AugeasNode getNode(AugeasTree tree) {
String resourceKey = resourceContext.getResourceKey();
int snmpIdx = getWwwServiceIndex();
ApacheServerComponent server = resourceContext.getParentResourceComponent();
if (snmpIdx < 1) {
throw new IllegalStateException("Could not determine the index of the virtual host [" + resourceKey
+ "] in the runtime configuration. This is very strange.");
}
if (snmpIdx == 1) {
return tree.getRootNode();
}
final List<AugeasNode> allVhosts = new ArrayList<AugeasNode>();
RuntimeApacheConfiguration.walkRuntimeConfig(new RuntimeApacheConfiguration.NodeVisitor<AugeasNode>() {
public void visitOrdinaryNode(AugeasNode node) {
if ("<VirtualHost".equalsIgnoreCase(node.getLabel())) {
allVhosts.add(node);
}
}
public void visitConditionalNode(AugeasNode node, boolean isSatisfied) {
}
}, tree, server.getCurrentProcessInfo(), server.getCurrentBinaryInfo(), server.getModuleNames(), false);
//transform the SNMP index into the index of the vhost
int idx = allVhosts.size() - snmpIdx + 1;
AugeasNode vhost = allVhosts.get(idx);
//now check if there are any If* directives underneath this vhost.
//we don't support configuring such beasts.
if (vhost.getChildByLabel("<IfDefine").isEmpty() && vhost.getChildByLabel("<IfModule").isEmpty()
&& vhost.getChildByLabel("<IfVersion").isEmpty()) {
return vhost;
} else {
throw new IllegalStateException("Configuration of the virtual host [" + resourceKey
+ "] contains conditional blocks. This is not supported by this plugin.");
}
}
/**
* Returns a directive corresponding to this component in the Apache directive tree.
*
* @param keepConditionalDirectives whether to keep conditional directives in the tree (true) or whether to
* transparently include/ignore their children based on runtime config info (false)
* @throws IllegalStateException if none or more than one directives found
*/
public ApacheDirective getDirective(boolean keepConditionalDirectives) {
ApacheDirectiveTree tree = resourceContext.getParentResourceComponent().parseRuntimeConfiguration(true,
keepConditionalDirectives);
String resourceKey = resourceContext.getResourceKey();
int snmpIdx = getWwwServiceIndex();
if (snmpIdx < 1) {
throw new IllegalStateException("Could not determine the index of the virtual host [" + resourceKey
+ "] in the runtime configuration. This is very strange.");
}
if (snmpIdx == 1) {
return tree.getRootNode();
}
final List<ApacheDirective> allVhosts = tree.findByName("<VirtualHost", true);
//transform the SNMP index into the index of the vhost
int idx = allVhosts.size() - snmpIdx + 1;
return allVhosts.get(idx);
}
/**
* @see ApacheServerComponent#finishConfigurationUpdate(ConfigurationUpdateReport)
*/
public void finishConfigurationUpdate(ConfigurationUpdateReport report) {
resourceContext.getParentResourceComponent().finishConfigurationUpdate(report);
}
/**
* @see ApacheServerComponent#conditionalRestart()
*
* @throws Exception
*/
public void conditionalRestart() throws Exception {
resourceContext.getParentResourceComponent().conditionalRestart();
}
public void deleteEmptyFile(AugeasTree tree, AugeasNode deletedNode) {
resourceContext.getParentResourceComponent().deleteEmptyFile(tree, deletedNode);
}
private void collectSnmpMetric(MeasurementReport report, int primaryIndex, SNMPSession snmpSession,
MeasurementScheduleRequest schedule) throws SNMPException {
SNMPValue snmpValue = null;
String metricName = schedule.getName();
int dotIndex = metricName.indexOf('.');
String mibName;
if (dotIndex == -1) {
// it's a service metric (e.g. "wwwServiceName") or a summary metric (e.g. "wwwSummaryInRequests")
mibName = metricName;
List<SNMPValue> snmpValues = snmpSession.getColumn(mibName);
// NOTE: We assume SNMPValue's are returned in index-order.
snmpValue = snmpValues.get(primaryIndex - 1);
} else {
// it's a request or response metric (e.g. "wwwRequestInRequests.GET" or "wwwResponseOutResponses.200")
mibName = metricName.substring(0, dotIndex);
String mibSecondaryIndex = metricName.substring(dotIndex + 1);
String oid;
try {
Integer.parseInt(mibSecondaryIndex);
oid = mibSecondaryIndex;
} catch (NumberFormatException e) {
// OID must be encoded as a string (e.g. 3.71.69.84 == "GET") - decode it
oid = convertStringToOid(mibSecondaryIndex);
}
boolean found = false;
Map<String, SNMPValue> table = snmpSession.getTable(mibName, primaryIndex);
if (table != null) {
snmpValue = table.get(oid);
if (snmpValue != null) {
found = true;
}
}
if (!found) {
LOG.error("Entry '" + oid + "' not found for " + mibName + "[" + primaryIndex + "].");
LOG.error("Table:\n" + table);
return;
}
}
LOG.debug("Collected SNMP metric [" + metricName + "], value = " + snmpValue);
boolean valueIsTimestamp = false;
ApacheServerComponent.addSnmpMetricValueToReport(report, schedule, snmpValue, valueIsTimestamp);
}
private String convertStringToOid(String string) {
String oid;
StringBuilder strBuf = new StringBuilder();
strBuf.append(string.length()); // first digit in OID is the length of the string
for (int i = 0; i < string.length(); i++) {
// remaining digits are the integer values of each of the characters in the string
strBuf.append('.').append((byte) string.charAt(i));
}
oid = strBuf.toString();
return oid;
}
public static int getWwwServiceIndex(ApacheServerComponent parent, String resourceKey) {
//figure out the servername and addresses of this virtual host
//from the resource key.
String vhostServerName = null;
String[] vhostAddressStrings = null;
int pipeIdx = resourceKey.indexOf('|');
if (pipeIdx >= 0) {
vhostServerName = resourceKey.substring(0, pipeIdx);
if (vhostServerName.isEmpty()) {
vhostServerName = null;
}
}
vhostAddressStrings = resourceKey.substring(pipeIdx + 1).split(" ");
int foundIdx = 0;
//only look for the vhost entry if the vhost we're looking for isn't the main server
if (!MAIN_SERVER_RESOURCE_KEY.equals(vhostAddressStrings[0])) {
ApacheDirectiveTree tree = parent.parseRuntimeConfiguration(false);
//find the vhost entry the resource key represents
List<ApacheDirective> vhosts = tree.search("/<VirtualHost");
for (ApacheDirective vhost : vhosts) {
List<ApacheDirective> serverNames = vhost.getChildByName("ServerName");
String serverName = serverNames.size() > 0 ? serverNames.get(0).getValuesAsString() : null;
List<String> addrs = vhost.getValues();
boolean serverNamesMatch =
(serverName == null && vhostServerName == null)
|| (serverName != null && serverName.equals(vhostServerName));
boolean addrsMatch = true;
if (addrs.size() != vhostAddressStrings.length) {
addrsMatch = false;
} else {
for (int i = 0; i < vhostAddressStrings.length; ++i) {
if (!addrs.contains(vhostAddressStrings[i])) {
addrsMatch = false;
break;
}
}
}
if (serverNamesMatch && addrsMatch) {
break;
}
++foundIdx;
}
if (foundIdx == vhosts.size()) {
LOG.debug("The virtual host with resource key [" + resourceKey
+ "] doesn't seem to be present in the apache configuration anymore.");
return -1;
} else {
//httpd vhosts are internally (in httpd internal data structures) ordered like this:
//1) the main server entry is always first
//2) all the vhosts are ordered from the last to appear in the joined config files to the first one
//we now have an index to the list of the vhosts in the order they are defined.
//so let's swap it over.
//just subtracting from the size will give us the "room" for the first index
//being the main host. In another words the below subtraction is correct even though
//you might think there's a 1-off bug there.
foundIdx = vhosts.size() - foundIdx;
}
}
//the snmp indices are 1-based
return foundIdx + 1;
}
/**
* @return the index of the virtual host that identifies it in SNMP
* @throws Exception on SNMP error
*/
private int getWwwServiceIndex() {
ConfigurationTimestamp currentTimestamp =
resourceContext.getParentResourceComponent().getConfigurationTimestamp();
if (!lastConfigurationTimeStamp.equals(currentTimestamp)) {
snmpWwwServiceIndex = -1;
//don't go through this configuration again even if we fail further below.. we'd fail again.
lastConfigurationTimeStamp = currentTimestamp;
//configuration has changed. re-read the service index of this virtual host
snmpWwwServiceIndex =
getWwwServiceIndex(resourceContext.getParentResourceComponent(), resourceContext.getResourceKey());
}
return snmpWwwServiceIndex;
}
private ResourceType getDirectoryResourceType() {
return resourceContext.getResourceType().getChildResourceTypes().iterator().next();
}
public boolean isAugeasEnabled() {
ApacheServerComponent parent = resourceContext.getParentResourceComponent();
return parent.isAugeasEnabled();
}
}