/** * 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) [2009-2010], VMware, 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.plugin.zimbra.five; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FilenameFilter; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hyperic.hq.product.AutoServerDetector; import org.hyperic.hq.product.LogFileTrackPlugin; import org.hyperic.hq.product.PluginException; import org.hyperic.hq.product.ServerControlPlugin; import org.hyperic.hq.product.ServerDetector; import org.hyperic.hq.product.ServerResource; import org.hyperic.hq.product.ServiceResource; import org.hyperic.util.config.ConfigResponse; import org.hyperic.util.exec.Execute; import org.hyperic.util.exec.PumpStreamHandler; public class ZimbraServerDetector extends ServerDetector implements AutoServerDetector { private static final String PROCESS_NAME = "zmmailboxdmgr"; private static final String PROCESS_DIR = "/libexec"; private static final String ZMSTATS_DIR = "zmstat"; private static final String PTQL_QUERY = "State.Name.eq={0}"; private static final String PROCESS_PID_QUERY = "Pid.SudoPidFile.eq={0}"; private static final String PROCESS_EXEC = "State.Name.eq={0}"; private static final String PROCESS_EXEC_2 = "State.Name.ct={0}"; // private static final String PROCESS_CHILD_QUERY = "State.Ppid.eq={0,number,#}"; private static Log log = LogFactory.getLog(ZimbraServerDetector.class); public List getServerResources(ConfigResponse platformConfig) throws PluginException { log.debug("getServerResources(" + platformConfig + ")"); Object args[] = {PROCESS_NAME}; long[] pids = getPids(MessageFormat.format(PTQL_QUERY, args)); List list_servers = new ArrayList(); for (int n = 0; n < pids.length; n++) { long pid = pids[n]; try { String exe = getProcExe(pid); String path = exe.substring(0, exe.length() - (PROCESS_DIR + File.separator + PROCESS_NAME).length()); log.debug("[getServerResources] proc: (" + pid + ") " + exe + " (" + path + ")"); // check version if (!checkVersion(path, getTypeInfo().getVersion())) { return list_servers; } //************** File zmStatsDir = new File(path, ZMSTATS_DIR); log.debug("[getServerResources] zmStatsDir=" + zmStatsDir.getCanonicalPath() + " (" + (zmStatsDir.exists() ? "OK" : "No OK") + ")"); if (zmStatsDir.exists()) { ConfigResponse productConfig = new ConfigResponse(); productConfig.setValue("installpath", path); ServerResource server = new ServerResource(); server.setType(this); server.setName(getPlatformName() + " " + getTypeInfo().getName()); server.setInstallPath(path); server.setIdentifier("zimbra " + path); // server.setProductConfig(productConfig); setProductConfig(server, productConfig); ConfigResponse metricConfig = new ConfigResponse(); metricConfig.setValue(LogFileTrackPlugin.PROP_FILES_SERVER, "log/mailbox.log"); server.setMeasurementConfig(metricConfig, LogFileTrackPlugin.LOGLEVEL_WARN, false); ConfigResponse controlConfig = new ConfigResponse(); controlConfig.setValue(ServerControlPlugin.PROP_PROGRAMPREFIX, "/usr/bin/sudo -u zimbra"); // RHEL 5.0 controlConfig.setValue(ServerControlPlugin.PROP_TIMEOUT, "300"); server.setControlConfig(controlConfig); list_servers.add(server); } } catch (Exception e) { log.error(e.getMessage(), e); } } return list_servers; } private final static Service[] SERVICES = { new Service("MySQL", "/db/mysql.pid"), new Service("Postfix", "/data/postfix/spool/pid/master.pid"), new Service("Log Watch", "/log/logswatch.pid"), new Service("Logger MySQL", "/logger/db/mysql.pid"), new Service("OpenLDAP", "/openldap/var/run/slapd.pid"), new Service("Swatch", "/log/swatch.pid"), new Service("MTA Config", "/log/zmmtaconfig.pid"), new Service("memcached", "/log/memcached.pid"), new Service("ClamAV", "/log/clamd.pid", "log/clamd.log"), new Service("Convertd Monitor", "/log/zmconvertdmon.pid", "log/zmconvertd.log"), new Service("Jetty Process", "/log/zmmailboxd_java.pid") }; private final static Service[] MULTI_SERVICES = { new Service("AMaViS", "/log/amavisd.pid", null, "amavisd"), new Service("HTTPD", "/log/httpd.pid"), new Service("NGINX", "/log/nginx.pid", "log/nginx.log"), new Service("Cyrus SASL", "/cyrus-sasl/state/saslauthd.pid") }; private final static Service[] OTHER_SERVICES = { new Service("MTAQueue Stats", "/zmstat/mtaqueue.csv"), new Service("VM Stats", "/zmstat/vm.csv") }; private ServiceResource createService(Service serviceData, String path) { ServiceResource service = new ServiceResource(); service.setServiceName(serviceData.getName()); service.setType(this, serviceData.getName()); service.setDescription(path); ConfigResponse metricConfig = new ConfigResponse(); if (serviceData.getLog() != null) { metricConfig.setValue(LogFileTrackPlugin.PROP_FILES_SERVICE, serviceData.getLog()); } else { metricConfig.setValue("service.log_track.enable", "false"); } service.setMeasurementConfig(metricConfig); return service; } protected List discoverServices(ConfigResponse config) throws PluginException { log.debug("discoverServices(" + config + ")"); List services = new ArrayList(); for (int n = 0; n < OTHER_SERVICES.length; n++) { Service serviceData = OTHER_SERVICES[n]; File csvFile = new File(config.getValue("installpath"), serviceData.getPidFile()); if (csvFile.exists()) { ServiceResource service = createService(serviceData, csvFile.getAbsolutePath()); ConfigResponse props = new ConfigResponse(); setProductConfig(service, props); service.setCustomProperties(new ConfigResponse()); services.add(service); } else { log.debug("OTHER_SERVICES '" + serviceData.getName() + "(" + serviceData.getPidFile() + ")' not found"); } } for (int n = 0; n < SERVICES.length; n++) { Service serviceData = SERVICES[n]; File pidFile = new File(config.getValue("installpath"), serviceData.getPidFile()); Object args[] = {pidFile.getAbsolutePath()}; String q = MessageFormat.format(PROCESS_PID_QUERY, args); long[] pids = getPids(q); if (pids.length > 0) { ServiceResource service = createService(serviceData, pidFile.getAbsolutePath()); ConfigResponse props = new ConfigResponse(); props.setValue("process.query", q); setProductConfig(service, props); service.setCustomProperties(new ConfigResponse()); services.add(service); } else { log.debug("SERVICES '" + serviceData.getName() + "(" + serviceData.getPidFile() + ")' not found"); } } for (int n = 0; n < MULTI_SERVICES.length; n++) { Service serviceData = MULTI_SERVICES[n]; log.debug("[discoverServices] -> " + serviceData.getName()); File pidFile = new File(config.getValue("installpath"), serviceData.getPidFile()); log.debug("pidFile='" + pidFile + "'"); Object args[] = {pidFile.getAbsolutePath()}; String pQuery = MessageFormat.format(PROCESS_PID_QUERY, args); log.debug("pQuery --> '" + pQuery + "'"); long[] pids = getPids(pQuery); if (pids.length > 0) { long pid = pids[0]; String exec = getProcExe(pid); log.debug("'" + serviceData.getName() + "' exec='" + exec + "'"); ServiceResource service = createService(serviceData, pidFile.getAbsolutePath()); ConfigResponse props = new ConfigResponse(); if (serviceData.getProcess() != null) { Object[] args2 = {serviceData.getProcess()}; props.setValue("process.query", MessageFormat.format(PROCESS_EXEC_2, args2)); } else { Object[] args2 = {new File(exec).getName()}; props.setValue("process.query", MessageFormat.format(PROCESS_EXEC, args2)); } props.setValue("process.status", pidFile.getAbsolutePath()); setProductConfig(service, props); log.debug("process.query = '" + props.getValue("process.query") + "'"); log.debug("process.status ='" + pidFile.getAbsolutePath() + "'"); service.setCustomProperties(new ConfigResponse()); services.add(service); } else { log.debug("MULTI_SERVICES '" + serviceData.getName() + "(" + serviceData.getPidFile() + ")' not found"); } log.debug("[discoverServices] <- " + serviceData.getName()); } long[] Convertd_pids = getPids("State.Name.eq=java,Args.*.eq=com.zimbra.cs.convertd.TransformationServer"); if (Convertd_pids.length > 0) { ServiceResource service = new ServiceResource(); service.setServiceName("Convertd"); service.setType(this, "Convertd"); ConfigResponse props = new ConfigResponse(); setProductConfig(service, props); ConfigResponse metricConfig = new ConfigResponse(); metricConfig.setValue(LogFileTrackPlugin.PROP_FILES_SERVICE, "log/convertd.log"); //relative to installpath service.setMeasurementConfig(metricConfig); service.setCustomProperties(new ConfigResponse()); services.add(service); } // Stats Process File dir_pids = new File(config.getValue("installpath"), "/zmstat/pid"); String[] pids_files = dir_pids.list(new PIDFilter()); for (int n = 0; n < pids_files.length; n++) { String pid_file = pids_files[n]; Object args[] = {pid_file}; String q = MessageFormat.format(PROCESS_PID_QUERY, args); long[] pids = getPids(q); if (pids.length > 0) { services.add(cerateStatService(pid_file)); } } return services; } private static Pattern p = Pattern.compile("zmstat-(.*).pid"); private ServiceResource cerateStatService(String file) { log.debug("cerateStatService('" + file + "')"); Matcher m = p.matcher(file); String name = file; if (m.matches()) { name = m.group(); } ServiceResource servicio = new ServiceResource(); servicio.setServiceName(name); servicio.setType(this, "Stats process"); servicio.setDescription(file); ConfigResponse props = new ConfigResponse(); props.setValue("pid-file", file); setProductConfig(servicio, props); ConfigResponse metricConfig = new ConfigResponse(); metricConfig.setValue("service.log_track.enable", "false"); servicio.setMeasurementConfig(metricConfig); servicio.setCustomProperties(new ConfigResponse()); return servicio; } private static class PIDFilter implements FilenameFilter { public boolean accept(File dir, String name) { return name.endsWith(".pid"); } } private boolean checkVersion(String path, String version) { boolean res = false; try { ByteArrayOutputStream output = new ByteArrayOutputStream(); Execute exec = new Execute(new PumpStreamHandler(output)); exec.setCommandline( new String[]{"sudo", "-u", "zimbra", path + "/bin/zmcontrol", "-v"}); int rc = exec.execute(); String out = output.toString().trim(); if (getLog().isDebugEnabled()) { getLog().debug("output of '" + path + "/bin/zmcontrol -v' : " + out); } if (rc == 0) { Pattern p = Pattern.compile(version.toLowerCase().replaceAll("x", "\\\\d*")); Matcher m = p.matcher(out); res = m.find(); if (!res) { getLog().debug("m -->" + m); } } } catch (Exception e) { getLog().warn("Could not get the version of mysql: " + e.getMessage(), e); } return res; } }