/* * 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.plugin.system; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.net.URL; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hyperic.hq.agent.AgentConfig; import org.hyperic.hq.common.shared.HQConstants; import org.hyperic.hq.appdef.shared.AppdefEntityConstants; import org.hyperic.hq.product.Collector; import org.hyperic.hq.product.ConfigFileTrackPlugin; import org.hyperic.hq.product.ControlPlugin; import org.hyperic.hq.product.ExecutableMeasurementPlugin; import org.hyperic.hq.product.ExecutableProcess; import org.hyperic.hq.product.GenericPlugin; import org.hyperic.hq.product.LogFileTailPlugin; import org.hyperic.hq.product.LogTrackPlugin; import org.hyperic.hq.product.PluginException; import org.hyperic.hq.product.PluginManager; import org.hyperic.hq.product.ProcessControlPlugin; import org.hyperic.hq.product.ProductPlugin; import org.hyperic.hq.product.ServerTypeInfo; import org.hyperic.hq.product.ServiceTypeInfo; import org.hyperic.hq.product.TypeBuilder; import org.hyperic.hq.product.TypeInfo; import org.hyperic.hq.product.Win32ControlPlugin; import org.hyperic.hq.product.Win32EventLogTrackPlugin; import org.hyperic.hq.product.Win32MeasurementPlugin; import org.hyperic.sigar.FileWatcherThread; import org.hyperic.sigar.ProcFileMirror; import org.hyperic.sigar.Sigar; import org.hyperic.sigar.SigarException; import org.hyperic.util.config.ConfigResponse; import org.hyperic.util.config.ConfigSchema; import org.hyperic.util.config.SchemaBuilder; import org.hyperic.util.config.StringConfigOption; public class SystemPlugin extends ProductPlugin { protected static final int DEPLOYMENT_ORDER = 0 ; public static final String NAME = "system"; public static final String FILE_SERVER_NAME = "FileServer"; public static final String NETWORK_SERVER_NAME = "NetworkServer"; //XXX we need a dummy server type for 'CPU' and 'Process' services. //would like to use one for both, this is probably not the best name. //suggestions welcome. public static final String PROCESS_SERVER_NAME = "ProcessServer"; public static final String WINDOWS_SERVER_NAME = "WindowsServer"; public static final String HYPERV_SERVER_NAME = "HyperVServer"; ///public static public static final String FS_NAME = "Mount"; public static final String PHYSICAL_DISK_NAME = "Physical Disk"; public static final String LOGICAL_DISK_NAME = "Logical Disk"; public static final String BLOCK_DEVICE_NAME = "Block Device"; public static final String FILE_NAME = "File"; public static final String SCRIPT_NAME = "Script"; public static final String DIR_NAME = "Directory"; public static final String DIR_TREE_NAME = "Directory Tree"; public static final String NETIF_NAME = "Interface"; public static final String CPU_NAME = "CPU"; public static final String PROCESS_NAME = "Process"; public static final String MPROCESS_NAME = "MultiProcess"; public static final String SVC_NAME = "Windows Service"; public static final String HYPERV_NETWORK_INTERFACE = "HyperV Network Interface"; public static final String HYPERV_PHYSICAL_DISK = "HyperV Physical Disk"; public static final String HYPERV_MEMORY = "Hyper-V Memory"; public static final String HYPERV_LOGICAL_PROCESSOR = "Hyper-V Logical Processor"; public static final String HYPERV_SERVICE_NAME = "vmms"; protected static Log log = LogFactory.getLog("SystemPlugin"); public static final String[] FILE_SERVICES = { FS_NAME, PHYSICAL_DISK_NAME, LOGICAL_DISK_NAME, BLOCK_DEVICE_NAME, FILE_NAME, DIR_NAME, DIR_TREE_NAME }; public static final String[] NETWORK_SERVICES = { NETIF_NAME, }; public static final String[] PROCESS_SERVICES = { CPU_NAME, PROCESS_NAME, MPROCESS_NAME }; public static final String[] HYPERV_SERVICES = { HYPERV_NETWORK_INTERFACE, HYPERV_PHYSICAL_DISK, HYPERV_MEMORY, HYPERV_LOGICAL_PROCESSOR }; public static final String FILE_MOUNT_SERVICE = TypeBuilder.composeServiceTypeName(FILE_SERVER_NAME, FS_NAME); public static final String PHYSICAL_DISK_SERVICE = TypeBuilder.composeServiceTypeName(FILE_SERVER_NAME, PHYSICAL_DISK_NAME); public static final String LOGICAL_DISK_SERVICE = TypeBuilder.composeServiceTypeName(FILE_SERVER_NAME, LOGICAL_DISK_NAME); public static final String BLOCK_DEVICE_SERVICE = TypeBuilder.composeServiceTypeName(FILE_SERVER_NAME, BLOCK_DEVICE_NAME); public static final String NETWORK_INTERFACE_SERVICE = TypeBuilder.composeServiceTypeName(NETWORK_SERVER_NAME, NETIF_NAME); public static final String SCRIPT_SERVICE = SCRIPT_NAME; public static final String FILE_SERVICE = TypeBuilder.composeServiceTypeName(FILE_SERVER_NAME, FILE_NAME); public static final String DIR_SERVICE = TypeBuilder.composeServiceTypeName(FILE_SERVER_NAME, DIR_NAME); public static final String DIR_TREE_SERVICE = TypeBuilder.composeServiceTypeName(FILE_SERVER_NAME, DIR_TREE_NAME); public static final String PROP_FS = "mount"; public static final String PROP_PATH = "path"; public static final String PROP_NETIF = "interface"; public static final String PROP_CPU = "cpu"; public static final String PROP_HYPERV_NETWORK_INTERFACE = "Network Interface"; public static final String PROP_HYPERV_PHYSICAL_DISK = "PhysicalDisk"; public static final String PROP_HYPERV_LOGICAL_PROCESSOR = "Hyper-V Hypervisor Logical Processor"; public static final String PROP_ENABLE_USER_AI = "autodiscover.users"; public static final String PROP_SVC = Win32ControlPlugin.PROP_SERVICENAME; public static final String PROP_ARGS = "args"; public SystemPlugin() { setName(NAME); } public void init(PluginManager manager) throws PluginException { super.init(manager); final String prop = "sigar.mirror.procnet"; final String enable = manager.getProperty(prop); getLog().debug(prop + "=" + enable); if (!"true".equals(enable)) { return; } //intended for use on systems with very large connection tables //where processing /proc/net/tcp may block or otherwise take much longer //than reading a plain 'ol text file //should only happen agent-side String dir = manager.getProperty(AgentConfig.PROP_TMPDIR[0]); ProcFileMirror mirror = null; final String[] procnet = { "/proc/net/tcp", "/proc/net/tcp6" }; for (int i=0; i<procnet.length; i++) { File file = new File(procnet[i]); if (!file.exists()) { continue; } if (mirror == null) { mirror = new ProcFileMirror(new Sigar(), dir); } try { mirror.add(file); getLog().debug("mirroring " + procnet[i]); } catch (SigarException e) { getLog().warn(e.getMessage()); } } if (mirror != null) { FileWatcherThread.getInstance().add(mirror); FileWatcherThread.getInstance().doStart(); } } @Override public GenericPlugin getPlugin(String type, TypeInfo info) { GenericPlugin res = aglyGetPlugin(type, info); log.info("[getPlugin] type='" + type + "' info='" + info.getName()+ "' res="+res); return res; } public GenericPlugin aglyGetPlugin(String type, TypeInfo info) { if (type.equals(ProductPlugin.TYPE_MEASUREMENT)) { if (info.getName().equals(SVC_NAME)) { return new Win32MeasurementPlugin(); } if (info.getName().equals(HYPERV_NETWORK_INTERFACE)) { return new HyperVMeasurementPlugin(); } if (info.getName().equals(HYPERV_PHYSICAL_DISK)) { return new HyperVMeasurementPlugin(); } if (info.getName().equals(HYPERV_MEMORY)) { return new Win32MeasurementPlugin(); } if (info.getName().equals(HYPERV_LOGICAL_PROCESSOR)) { return new HyperVMeasurementPlugin(); } if ((info.getType() == TypeInfo.TYPE_SERVER) && ((ServerTypeInfo)info).isVirtual()) { //virtual server, no metrics. return null; } if (info.getName().equals(SCRIPT_NAME)) { return new ExecutableMeasurementPlugin(); } else { return new SystemMeasurementPlugin(); } } else if (type.equals(ProductPlugin.TYPE_AUTOINVENTORY)) { switch (info.getType()) { case TypeInfo.TYPE_PLATFORM: return new SigarPlatformDetector(this.hasPlatformControlActions()); case TypeInfo.TYPE_SERVER: if (info.getName().equals(FILE_SERVER_NAME)) { return new FileSystemDetector(); } else if (info.getName().equals(NETWORK_SERVER_NAME)) { return new NetifDetector(); } else if (info.getName().equals(PROCESS_SERVER_NAME)) { return new ProcessorDetector(); } else if (info.getName().equals(WINDOWS_SERVER_NAME)) { return new WindowsDetector(); } else if (info.getName().equals(HYPERV_SERVER_NAME)) { return new HypervDetector(); } } } else if (type.equals(ProductPlugin.TYPE_CONTROL)) { if (info.isService(FILE_NAME)) { return new FileControlPlugin(); } else if (info.getName().equals(SVC_NAME)) { return new Win32ControlPlugin(); } else if (info.getName().equals(PROCESS_NAME)) { return new ProcessControlPlugin(); } } else if (type.equals(ProductPlugin.TYPE_CONFIG_TRACK)) { if ((info.getType() == TypeInfo.TYPE_PLATFORM) || info.getName().equals(PROCESS_NAME)) { return new ConfigFileTrackPlugin(); } } else if (type.equals(ProductPlugin.TYPE_LOG_TRACK)) { if (info.getType() == TypeInfo.TYPE_PLATFORM) { if (info.isWin32Platform()) { return new WindowsLogTrackPlugin(); } else { return new UnixLogTrackPlugin(); } } else if (info.getName().equals(SVC_NAME)) { return new Win32EventLogTrackPlugin(); } else if (info.isService(FILE_NAME)) { return new FileServiceLogPlugin(); } else if (info.getName().equals(PROCESS_NAME)) { return new LogFileTailPlugin(); } else if (info.getName().equals(SCRIPT_NAME) || info.isService(FS_NAME)) { return new LogTrackPlugin(); } } else if (type.equals(ProductPlugin.TYPE_LIVE_DATA)) { if (info.getType() == TypeInfo.TYPE_PLATFORM) { return new SystemLiveDataPlugin(); } } return null; } protected boolean hasPlatformControlActions() { return false ; }//EOM private static final String[][] PLAT_CPROPS = { { "arch", "Architecture" }, { "version", "OS Version" }, { "vendor", "Vendor" }, { "vendorVersion", "Vendor Version" }, { "ram", "RAM" }, { "cpuSpeed", "CPU Speed" }, { "ip", "IP Address" }, { "primaryDNS", "Primary DNS" }, { "secondaryDNS", "Secondary DNS" }, { "defaultGateway", "Default Gateway" }, { HQConstants.MOID, "MOID" }, { HQConstants.VCUUID, "VCenter UUID" } }; private static final String[][] NETIF_CPROPS = { { "mtu", "Maximum Transmission Unit" }, { "flags", "Interface Flags" }, { "mac", "MAC Address" }, { "address", "IP Address" }, { "netmask", "Netmask" }, { "broadcast", "Broadcast Address" }, }; private static final String[][] FILE_CPROPS = { { "md5", "Message Digest" }, { "fs", "File System" }, { "permissions", "Permissions" }, { "user", "User" }, { "group", "Group" } }; private static final String[][] PROCESS_CPROPS = { { "user", "User" }, { "group", "Group" }, { "exe", "Executable" }, { "cwd", "Current Working Directory" } }; private static final String[][] SVC_CPROPS = { { "path", "Path to executable" }, { "startupType", "Startup type" }, { "displayName", "Display name" }, }; public ConfigSchema getCustomPropertiesSchema(String name) { ConfigSchema schema = new ConfigSchema(); String[][] cprops; if (SigarPlatformDetector.isSupportedPlatform(name)) { cprops = PLAT_CPROPS; } else if (name.equals(NETWORK_INTERFACE_SERVICE)) { cprops = NETIF_CPROPS; } else if (name.equals(FILE_SERVICE) || name.equals(SCRIPT_SERVICE) || name.equals(DIR_SERVICE)) { cprops = FILE_CPROPS; } else if (name.equals(PROCESS_NAME)) { cprops = PROCESS_CPROPS; } else if (name.equals(SVC_NAME)) { cprops = SVC_CPROPS; } else { return schema; } for (int i=0; i<cprops.length; i++) { StringConfigOption opt = new StringConfigOption(cprops[i][0], cprops[i][1]); schema.addOption(opt); } return schema; } private void addWindowsService(TypeBuilder types) { /* we dont use TypeBuilder here because we dont want * the virtual server name as part of the service name */ ServerTypeInfo server = new ServerTypeInfo(WINDOWS_SERVER_NAME, WINDOWS_SERVER_NAME, TypeBuilder.NO_VERSION); server.setValidPlatformTypes(TypeBuilder.WIN32_PLATFORM_NAMES); server.setVirtual(true); ServiceTypeInfo service = new ServiceTypeInfo(SVC_NAME, SVC_NAME, server); types.add(server); types.add(service); } private void addHyperVService(TypeBuilder types) { /* we dont use TypeBuilder here because we dont want * the virtual server name as part of the service name */ ServerTypeInfo server = new ServerTypeInfo(HYPERV_SERVER_NAME, HYPERV_SERVER_NAME, TypeBuilder.NO_VERSION); server.setVirtual(true); types.add(server); for (int i=0; i<HYPERV_SERVICES.length; i++) { String name = HYPERV_SERVICES[i]; ServiceTypeInfo service = new ServiceTypeInfo(name, name, server); types.add(service); } } private void addProcessServices(TypeBuilder types) { /* we dont use TypeBuilder here because we dont want * the virtual server name as part of the service name */ ServerTypeInfo server = new ServerTypeInfo(PROCESS_SERVER_NAME, PROCESS_SERVER_NAME, TypeBuilder.NO_VERSION); server.setVirtual(true); types.add(server); for (int i=0; i<PROCESS_SERVICES.length; i++) { String name = PROCESS_SERVICES[i]; ServiceTypeInfo service = new ServiceTypeInfo(name, name, server); types.add(service); } } public TypeInfo[] getTypes() { TypeBuilder types = new TypeBuilder(); String[] platforms = TypeBuilder.ALL_PLATFORM_NAMES; for (int i=0; i<platforms.length; i++) { types.addPlatform(platforms[i]); } ServerTypeInfo server; server = types.addServer(FILE_SERVER_NAME, TypeBuilder.NO_VERSION); server.setVirtual(true); server.setDescription("Platform File Server"); types.addServices(server, FILE_SERVICES); ServiceTypeInfo script = new ServiceTypeInfo(SCRIPT_NAME, SCRIPT_NAME, server); types.add(script); server = types.addServer(NETWORK_SERVER_NAME, TypeBuilder.NO_VERSION); server.setVirtual(true); server.setDescription("Platform Network Server"); types.addServices(server, NETWORK_SERVICES); addProcessServices(types); addWindowsService(types); addHyperVService(types); return types.getTypes(); } public ConfigSchema getConfigSchema(TypeInfo info, ConfigResponse config) { SchemaBuilder schema = new SchemaBuilder(config); log.debug("[getConfigSchema] info="+info); switch (info.getType()) { case TypeInfo.TYPE_PLATFORM: //XXX does not work //schema.add(PROP_ENABLE_USER_AI, // "Enable autodiscovery of user services", // false); break; case TypeInfo.TYPE_SERVICE: if (info.isService(FS_NAME)) { schema.add(PROP_FS, "File System Mount", "/"); } else if (info.isService(PHYSICAL_DISK_NAME)) { schema.add("name", "Instace Name", ""); } else if (info.isService(LOGICAL_DISK_NAME)) { schema.add("name", "Instace Name", ""); } else if (info.isService(BLOCK_DEVICE_NAME)) { schema.add("name", "Instace Name", ""); } else if (info.isService(FILE_NAME)) { schema.add(PROP_PATH, "Path to File", "../../log/agent.log"); } else if (info.isService(DIR_NAME) || info.isService(DIR_TREE_NAME)) { schema.add(PROP_PATH, "Path to Directory", "data"); } else if (info.isService(NETIF_NAME)) { schema.add(PROP_NETIF, "Network Interface", "eth0"); } else if (info.getName().equals(CPU_NAME)) { schema.add(PROP_CPU, "CPU", "0"); } else if (info.getName().equals(PROCESS_NAME)) { schema.add(SystemMeasurementPlugin.PTQL_CONFIG, "Process Query", "State.Name.eq=java"); } else if (info.getName().equals(MPROCESS_NAME)) { schema.add(SystemMeasurementPlugin.PTQL_CONFIG, "Multi Process Query", "State.Name.eq=httpd"); } else if (info.getName().equals(HYPERV_NETWORK_INTERFACE) ) { schema.add(PROP_HYPERV_NETWORK_INTERFACE, HYPERV_NETWORK_INTERFACE + " Name", "/"); } else if (info.getName().equals(HYPERV_PHYSICAL_DISK) ) { schema.add(PROP_HYPERV_PHYSICAL_DISK, HYPERV_PHYSICAL_DISK + " Name", "/"); } else if (info.getName().equals(HYPERV_LOGICAL_PROCESSOR) ) { schema.add(PROP_HYPERV_LOGICAL_PROCESSOR, HYPERV_LOGICAL_PROCESSOR + " Name", "/"); } else if (info.getName().equals(SVC_NAME)) { schema.add(PROP_SVC, SVC_NAME + " Name", ""); } else if(info.getName().equals(SCRIPT_NAME)) { schema.add("prefix", "Prefix arguments to script", "").setOptional(true); schema.add(PROP_PATH, "Script Name", "/path/to/script"); schema.add(ExecutableProcess.PROP_ARGS, "Script Arguments", "").setOptional(true); schema.add(ExecutableProcess.PROP_TIMEOUT, "Script Timeout (in seconds)", 120); } break; } return schema.getSchema(); } /** * This plugin has to be loaded first. Almost all other plugins require the OS platforms to be parents of their server types */ protected int getDeploymentOrder() { return DEPLOYMENT_ORDER ; } }