/* * NOTE: This copyright does *not* cover user programs that use HQ * program services by normal system calls through the application * program interfaces provided as part of the Hyperic Plug-in Development * Kit or the Hyperic Client Development Kit - this is merely considered * normal use of the program, and does *not* fall under the heading of * "derived work". * * Copyright (C) [2004, 2005, 2006], Hyperic, Inc. * This file is part of HQ. * * HQ is free software; you can redistribute it and/or modify * it under the terms version 2 of the GNU General Public License 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. */ package org.hyperic.hq.product; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; import org.hyperic.hq.measurement.MeasurementConstants; import org.hyperic.hq.product.pluginxml.PluginData; import org.hyperic.util.config.ConfigResponse; import org.hyperic.util.config.ConfigSchema; import org.hyperic.util.filter.TokenReplacer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Define and collect metrics. * */ public class MeasurementPlugin extends GenericPlugin { public static final String PROP_TEMPLATE_CONFIG = "template-config"; public static final String TYPE_COLLECTOR = "collector"; //plugin type private static final String HELP_PLATFORM_ALL = "ALL"; private Log log = LogFactory.getLog(this.getClass().getName()); private Class collector = null; private static boolean inProxyRegister = false; private MeasurementPluginManager manager; private static final int PLATFORM_HELP_UNIX = 1; private static final int PLATFORM_HELP_WIN32 = 2; //properties to assist with help text portability private static final String[][] PLATFORM_HELP_PROPS = { { "CMD.cp", "cp", "copy" }, { "CMD.cpr", "cp -R", "xcopy /E" }, { "CMD.prompt", "%", "C:\\>" }, { "CMD.env.set", "export", "set" }, { "CMD.ext", "sh", "exe" }, { "CMD.rm", "rm", "del" }, { "FILE.sep", "/", "\\" }, { "FILE.sep.esc", "/", "\\\\" }, //for .properties files }; public MeasurementPlugin() {} private void registerProxies(PluginManager manager) throws PluginException { if (inProxyRegister) { return; //prevent recursion } //allow plugins to register proxy domains via plugin.xml if (getTypeInfo() == null) { return; //this is already a proxy } String domain = getTypeProperty("DOMAIN"); if (domain == null) { return; } inProxyRegister = true; StringTokenizer tok = new StringTokenizer(domain, ","); while (tok.hasMoreTokens()) { String name = tok.nextToken(); if (this.manager.isRegistered(name)) { continue; } getLog().info("Register " + getName() + " proxy for domain: " + name); try { getManager().createPlugin(name, this); } catch (PluginException e) { inProxyRegister = false; throw e; } } inProxyRegister = false; } public void init(PluginManager manager) throws PluginException { super.init(manager); this.manager = (MeasurementPluginManager)manager; registerProxies(manager); } protected MeasurementPluginManager getManager() { return this.manager; } /** * Allow xml template properties to be added by a plugin. * The properties can be used as if the hq-plugin.xml * defined <property name="..." value="..."/> * for each entry in the Map returned. Properties with the * same name in the xml file will override these. */ protected Map getMeasurementProperties() { return null; } private void setInterval(MeasurementInfo metric) { // non-avail metric, avail set based on TypeInfo in // MeasurementPlugin switch (metric.getCollectionType()) { case MeasurementConstants.COLL_TYPE_DYNAMIC: metric.setInterval(MeasurementConstants.INTERVAL_DYNAMIC); break; case MeasurementConstants.COLL_TYPE_TRENDSUP: case MeasurementConstants.COLL_TYPE_TRENDSDOWN: metric.setInterval(MeasurementConstants.INTERVAL_TRENDING); break; case MeasurementConstants.COLL_TYPE_STATIC: metric.setInterval(MeasurementConstants.INTERVAL_STATIC); break; default: // Shouldn't happen.. should maybe blow up on this. log.error("Unknown collection type: " + metric.getCollectionType()); } } private void setAvailInterval(TypeInfo type, MeasurementInfo metric) { //set availability metric based on resource type switch (type.getType()) { case TypeInfo.TYPE_PLATFORM: metric.setInterval(MeasurementConstants.INTERVAL_AVAIL_PLAT); break; case TypeInfo.TYPE_SERVER: metric.setInterval(MeasurementConstants.INTERVAL_AVAIL_SVR); break; case TypeInfo.TYPE_SERVICE: metric.setInterval(MeasurementConstants.INTERVAL_AVAIL_SVC); break; default: //XXX: blow up? should never happen log.error("Unable to set default metric interval: " + "Unknown type: " + type); } } public MeasurementInfo[] getMeasurements(TypeInfo info) { if (this.data == null) { log.debug(getName() + " has no PluginData"); return null; } List xmlMetrics = this.data.getMetrics(info.getName()); if (xmlMetrics == null) { return null; } //append template-config if any String tmplConfig = this.data.getProperty(PROP_TEMPLATE_CONFIG); final String availCat = MeasurementConstants.CAT_AVAILABILITY; List metrics = new ArrayList(xmlMetrics.size()); for (int i=0; i<xmlMetrics.size(); i++) { MeasurementInfo metric = (MeasurementInfo)xmlMetrics.get(i); //clone due to modifications made below and in generateRateMetric metric = (MeasurementInfo)metric.clone(); metrics.add(metric); if (metric.getInterval() == -1) { if (metric.getCategory().equals(availCat)) { setAvailInterval(info, metric); } else { setInterval(metric); } } String template = metric.getTemplate(); if (tmplConfig != null) { String ptqlDomain = SigarMeasurementPlugin.PTQL_DOMAIN; //XXX only case atm is mysql plugin defines template-config //but also has sigar.ptql metrics, which break if this is appended if (!template.startsWith(ptqlDomain)) { metric.setTemplate(template + ":" + tmplConfig); template = metric.getTemplate(); } } //templates require the plugin name prefix String prefix = info.getName() + ":"; if (!template.startsWith(prefix)) { metric.setTemplate(prefix + template); } //NOTE: this currently needs to happen here, //after the template-config has been appended. if ((metric = generateRateMetric(metric)) != null) { metrics.add(metric); } } MeasurementInfo[] infos = new MeasurementInfo[metrics.size()]; metrics.toArray(infos); return infos; } private static final String[][] NO_PLATFORM_HELP_PROPS = new String[0][0]; protected String[][] getPlatformHelpProperties() { return NO_PLATFORM_HELP_PROPS; } String replaceHelpProperties(String help, TypeInfo info, Map props) { String platform = getHelpPlatform(info); TokenReplacer replacer = new TokenReplacer(); if ((info.getType() != TypeInfo.TYPE_PLATFORM) && (info.getVersion() != null)) { replacer.addFilter("product.version", info.getVersion()); } int ix; if (PlatformDetector.isWin32(platform)) { ix = PLATFORM_HELP_WIN32; } else { ix = PLATFORM_HELP_UNIX; } for (int i=0; i<PLATFORM_HELP_PROPS.length; i++) { replacer.addFilter(PLATFORM_HELP_PROPS[i][0], PLATFORM_HELP_PROPS[i][ix]); } String[][] pluginProps = getPlatformHelpProperties(); for (int i=0; i<pluginProps.length; i++) { replacer.addFilter(pluginProps[i][0], pluginProps[i][ix]); } replacer.addFilters(props); // Add all <property> tags from hq-plugin.xml replacer.addFilters(getProperties()); replacer.addFilters(getTypeProperties()); return replacer.replaceTokens(help); } /** * If the MeasurementInfo collection type is "trendsup" and * rate attribute is not set to "none" then create a new metric: * - name changed to original name plus the rate. * - alias changed to original name plus the rate. * - collection type is changed to "dynamic" * - turn off designate for the original metric. * - append __RATE__=$rate to the measurement template. */ private static MeasurementInfo generateRateMetric(MeasurementInfo info) { if (info.getCollectionType() != MeasurementConstants.COLL_TYPE_TRENDSUP) { return null; } if (info.getRate().equals(MeasurementInfo.NO_RATE)) { return null; } MeasurementInfo rate = (MeasurementInfo)info.clone(); if (info.isIndicator()) { info.setIndicator(false); //cant have more than one } if (info.isDefaultOn()) { info.setDefaultOn(false); //turn off raw counter by default } info = null; //just making sure we don't use this again. if ("".equals(rate.getRate())) { rate.setRate(MeasurementInfo.DEFAULT_RATE); } rate.setCollectionType(MeasurementConstants.COLL_TYPE_DYNAMIC); rate.setName(rate.getName() + " " + rate.getReadableRate()); rate.setAlias(rate.getAlias() + rate.getRate()); rate.setTemplate(rate.getTemplate() + MeasurementInfo.RATE_KEY + "=" + rate.getRate()); return rate; } protected String getPluginXMLHelp(TypeInfo info, String name, Map props) { if (this.data == null) { log.debug(getName() + " has no PluginData"); return null; } String help = this.data.getHelp(name); if (help != null) { if (help.length() == 0) { //make sure UI doesnt anchor to help that does not exist return null; } return replaceHelpProperties(help, info, props); } return help; } private String getHelpPlatform(TypeInfo info) { String[] platforms; String platform = HELP_PLATFORM_ALL; if (info.getType() == TypeInfo.TYPE_PLATFORM) { platforms = new String[0]; } else { platforms = info.getPlatformTypes(); } if (platforms.length == 1) { platform = platforms[0]; } return platform; } private String[] getHelpPlatformNames(TypeInfo info, String name) { String platform = getHelpPlatform(info); String[] names; if (platform.equals(HELP_PLATFORM_ALL)) { names = new String[] { name }; } else { //look for platform specific help first, e.g. Win32 names = new String[] { TypeBuilder.composePlatformTypeName(name, platform), name }; } return names; } private String[] getHelpPlatformNames(TypeInfo info) { return getHelpPlatformNames(info, info.getName()); } public String getHelp(TypeInfo info, Map props) { String[] names = getHelpPlatformNames(info); for (int i=0; i<names.length; i++) { String help = getPluginXMLHelp(info, names[i], props); if (help != null) { return help; } } return null; } /** * This method is called when the plugin is asked for a * metric value. The Metric is a translated value as returned * by the getMeasurements() routine, and then run through the * translate() method. * * @param metric Value returned from translate(), representing a * specific metric to retrieve * * @return The value of the Metric and timestamp of collection time * * @throws MetricInvalidException The plugin is unable to use the metric, * generally a developer bug where the template is malformed. * I.e. JMX MalformedObjectNameException * * @throws MetricNotFoundException The monitored resource does not know * about the requested Metric. I.e. JMX AttributeNotFoundException * * @throws MetricUnreachableException The monitored resource is unreachable. * I.e. ConnectException * * @throws PluginException Thrown when an internal plugin error occurs */ public MetricValue getValue(Metric metric) throws PluginException, MetricNotFoundException, MetricUnreachableException { //if we reach this point, this resource type //must have a <plugin type="collector" ...> defined. return Collector.getValue(this, metric); } public Collector getNewCollector() { if (this.collector == null) { if (this.data != null) { String name = this.data.getPlugin(TYPE_COLLECTOR, getTypeInfo().getName()); if (name == null) { String msg = "No measurement plugin or collector defined for: " + getTypeInfo().getName(); throw new MetricInvalidException(msg); } this.collector = ProductPlugin.getPluginClass(this.getClass().getClassLoader(), this.data, name, getTypeInfo().getName()); if (this.collector == null) { String msg = "Class '" + name + "' NotFound using ClassLoader=" + this.data.getClassLoader(); throw new MetricInvalidException(msg); } } } try { return (Collector)this.collector.newInstance(); } catch (Exception e) { throw new MetricInvalidException(e.getMessage()); } } public Properties getCollectorProperties(Metric metric) { return metric.getObjectProperties(); } /** * Translate a measurement as returned from getMeasurements() into a * value which can be passed into the plugin's getValue() routine. * * @param template Measurement template from one of the plugins * measurements returned from getMeasurements() * @param config Configuration used to perform translation on the * template * * @throws PluginException When an internal plugin error occurs * @throws MetricInvalidException When the template passed cannot * be mapped to a template returned * via getMeasurements() */ public String translate(String template, ConfigResponse config){ TokenReplacer replacer = new TokenReplacer(); Map props = getMeasurementProperties(); if (props != null) { replacer.addFilters(props); } replacer.addFilters(PluginData.getGlobalProperties()); template = replacer.replaceTokens(template); return Metric.translate(template, config); } public ConfigSchema getConfigSchema(TypeInfo info, ConfigResponse config) { if (this.data != null) { ConfigSchema schema = this.data.getConfigSchema(info, ProductPlugin.CFGTYPE_IDX_MEASUREMENT); if (schema != null) { return schema; } } return super.getConfigSchema(info, config); } }