/*
* 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.mssql;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.hq.product.*;
import org.hyperic.sigar.win32.*;
import org.hyperic.util.config.ConfigResponse;
public class MsSQLDetector extends ServerDetector implements AutoServerDetector {
static final String PROP_DB = "db.name";
private static final String DB_NAME = "Database";
static final String DEFAULT_SQLSERVER_SERVICE_NAME = "MSSQLSERVER";
static final String DEFAULT_SQLAGENT_SERVICE_NAME = "SQLSERVERAGENT";
static final String MS_CLUSTER_DISCOVERY = "MS_CLUSTER_DISCOVERY";
private static final Log log = LogFactory.getLog(MsSQLDetector.class);
public List getServerResources(ConfigResponse platformConfig) throws PluginException {
List cfgs;
try {
cfgs = Service.getServiceConfigs("sqlservr.exe");
} catch (Win32Exception e) {
debug(log,"[getServerResources] Error: " + e.getMessage(), e);
return null;
}
debug(log,"[getServerResources] MSSQL Server found:'" + cfgs.size() + "'");
if (cfgs.size() == 0) {
return null;
}
List servers = new ArrayList();
for (int i = 0; i < cfgs.size(); i++) {
ServiceConfig serviceConfig = (ServiceConfig) cfgs.get(i);
String name = serviceConfig.getName();
Service mssqlService = null;
boolean serverIsRunning = false;
try {
mssqlService = new Service(name);
if (mssqlService.getStatus() != Service.SERVICE_RUNNING) {
debug(log,"[getServerResources] service '" + name + "' is not RUNNING (status='"
+ mssqlService.getStatusString() + "')");
} else {
serverIsRunning = true;
}
} catch (Win32Exception e) {
debug(log,"[getServerResources] Error getting '" + name + "' service information " + e, e);
} finally {
if (mssqlService != null) {
mssqlService.close();
}
}
if (serverIsRunning) {
String instance = instaceName(name);
File dir = new File(serviceConfig.getExe()).getParentFile();
boolean correctVersion = false;
String regKey = getTypeProperty("regKey");
if (regKey != null) {
try {
regKey = regKey.replace("%NAME%", instance);
debug(log,"[getServerResources] regKey:'" + regKey + "'");
RegistryKey key = RegistryKey.LocalMachine.openSubKey(regKey);
String version = key.getStringValue("CurrentVersion");
String expectedVersion = getTypeProperty("version");
correctVersion = Pattern.compile(expectedVersion).matcher(version).find();
debug(log,"[getServerResources] server:'" + instance + "' version:'" + version + "' expectedVersion:'" + expectedVersion + "' correctVersion:'" + correctVersion + "'");
} catch (Win32Exception ex) {
debug(log,"[getServerResources] Error accesing to windows registry to get '" + instance + "' version. " + ex.getMessage());
}
} else {
correctVersion = checkVersionOldStyle(dir);
}
if (correctVersion) {
dir = dir.getParentFile(); //strip "Binn"
ServerResource server = createServerResource(dir.getAbsolutePath(), name);
servers.add(server);
}
}
}
return servers;
}
private boolean checkVersionOldStyle(File dir) {
String versionFile = getTypeProperty("mssql.version.file");
File dll = new File(dir, versionFile);
boolean correctVersion = dll.exists();
getLog().debug("[checkVersionOldStyle] dll:'" + dll + "' correctVersion='" + correctVersion + "'");
return correctVersion;
}
private ServerResource createServerResource(String installpath, String name) {
ServerResource server = createServerResource(installpath);
String instance = instaceName(name);
server.setName(server.getName() + " " + instance);
ConfigResponse cfg = new ConfigResponse();
cfg.setValue(Win32ControlPlugin.PROP_SERVICENAME, name);
String discoverMsCluster = getTypeProperty(MS_CLUSTER_DISCOVERY);
if (discoverMsCluster != null) {
Properties mssqlClusterPropes = ClusterDetect.getMssqlClusterProps(instance);
if (mssqlClusterPropes != null) {
cfg.setValue("mssql-cluster-name", mssqlClusterPropes.getProperty(ClusterDetect.CLUSTER_NAME_PROP));
cfg.setValue("virtual-platform-name", mssqlClusterPropes.getProperty(ClusterDetect.NETWORK_NAME_PROP));
cfg.setValue("sqlserver_name", mssqlClusterPropes.getProperty(ClusterDetect.NETWORK_NAME_PROP));
cfg.setValue("cluster-nodes", mssqlClusterPropes.getProperty(ClusterDetect.NODES_PROP));
cfg.setValue("instance-name", instance);
cfg.setValue("original-platform-name", getPlatformName());
} else {
cfg.setValue("sqlserver_name", getPlatformName());
}
}
server.setProductConfig(cfg);
server.setMeasurementConfig();
server.setControlConfig();
return server;
}
private static String instaceName(String name) {
String instance = name;
if (name.startsWith("MSSQL$")) {
instance = name.substring(6);
}
return instance;
}
private static int getServiceStatus(String name) {
Service svc = null;
try {
svc = new Service(name);
debug(log,"[getServiceStatus] name='" + name + "' status='" + svc.getStatusString() + "'");
return svc.getStatus();
} catch (Win32Exception e) {
debug(log,"[getServiceStatus] name='" + name + "' " + e);
return Service.SERVICE_STOPPED;
} finally {
if (svc != null) {
svc.close();
}
}
}
@Override
protected List discoverServices(ConfigResponse serverConfig) throws PluginException {
ArrayList services = new ArrayList();
Map<String, String> dbsDisk = new HashMap<String, String>();
Map<String, String> dbsFile = new HashMap<String, String>();
List<String> dbsFileNamesCMD = MsSQLDataBaseCollector.prepareSqlCommand(serverConfig.toProperties());
dbsFileNamesCMD.add("-Q");
dbsFileNamesCMD.add("SELECT name,filename FROM master..sysdatabases");
List<List<String>> res;
try {
res = MsSQLDataBaseCollector.executeSqlCommand(dbsFileNamesCMD);
for (List<String> line : res) {
if (line.size() == 2) {
String path = line.get(1);
final String db = line.get(0);
debug(log,"===> " + db + " = " + path);
int i = path.indexOf("\\");
if (i != -1) {
dbsDisk.put(db, path.substring(0, i));
dbsFile.put(db, path);
}
}
}
} catch (PluginException ex) {
log.error("Unable to connect to the DB, review the user/password/sqlserver_name/instance options.", ex);
return services;
}
debug(log,"===> dbsDisk = " + dbsDisk);
debug(log,"===> dbsFile = " + dbsFile);
if (dbsDisk.isEmpty()) {
log.error("Unable to connect to the DB, review the user/password/sqlserver_name/instance options.");
return services;
}
String sqlServerServiceName = serverConfig.getValue(Win32ControlPlugin.PROP_SERVICENAME, DEFAULT_SQLSERVER_SERVICE_NAME);
List<ServiceInfo> servicesNames = new ArrayList<ServiceInfo>();
String sqlServerMetricPrefix = "SQLServer"; // metric prefix in case of default instance
String msrsPrefix = "MSRS 2014 Windows Service";
String instaceName = DEFAULT_SQLSERVER_SERVICE_NAME;
if (getTypeInfo().getVersion().equals("2012")) {
msrsPrefix = "MSRS 2011 Windows Service";
} else if (getTypeInfo().getVersion().equals("2008")) {
msrsPrefix = "MSRS 2008 Windows Service";
} else if (getTypeInfo().getVersion().equals("2008 R2")) {
msrsPrefix = "MSRS 2008 R2 Windows Service";
} else if (getTypeInfo().getVersion().equals("2005")) {
msrsPrefix = "MSRS 2005 Windows Service";
}
if (sqlServerServiceName.equals(DEFAULT_SQLSERVER_SERVICE_NAME)) { // single instance
String rpPrefix = "ReportServer";
String olapPrefix = "MSAS12";
if (getTypeInfo().getVersion().startsWith("2012")) {
olapPrefix = "MSAS11";
}
if (getTypeInfo().getVersion().startsWith("2008")) {
olapPrefix = "MSAS 2008";
} else if (getTypeInfo().getVersion().equals("2005")) {
olapPrefix = "MSAS 2005";
}
servicesNames.add(new ServiceInfo("SQLSERVERAGENT", "SQLAgent", "SQLAgent", "SQLAgent"));
servicesNames.add(new ServiceInfo("ReportServer", "Report Server", rpPrefix, "Report Server"));
servicesNames.add(new ServiceInfo("MSSQLServerOLAPService", "Analysis Services", olapPrefix, "Analysis Services"));
} else { // multiple instances
instaceName = sqlServerServiceName.substring(sqlServerServiceName.indexOf("$") + 1);
sqlServerMetricPrefix = sqlServerServiceName;
servicesNames.add(new ServiceInfo("SQLAgent$" + instaceName, "SQLAgent", "SQLAgent$" + instaceName, "SQLAgent"));
servicesNames.add(new ServiceInfo("ReportServer$" + instaceName, "Report Server", "ReportServer$" + instaceName, "Report Server"));
servicesNames.add(new ServiceInfo("MSOLAP$" + instaceName, "Analysis Services", "MSOLAP$" + instaceName, "Analysis Services"));
}
for (int i = 0; i < servicesNames.size(); i++) {
ServiceInfo s = servicesNames.get(i);
if (getServiceStatus(s.winServiceName) == Service.SERVICE_RUNNING) {
debug(log,"[discoverServices] service='" + s.winServiceName + "' runnig");
ServiceResource agentService = new ServiceResource();
agentService.setType(this, s.type);
agentService.setServiceName(s.serviceName);
ConfigResponse cfg = new ConfigResponse();
cfg.setValue(Win32ControlPlugin.PROP_SERVICENAME, s.winServiceName);
cfg.setValue("pref_prefix", s.metricsPrefix);
if (s.type.equals("Report Server")) {
cfg.setValue("MSRS", msrsPrefix);
cfg.setValue("instance", instaceName);
}
agentService.setProductConfig(cfg);
agentService.setMeasurementConfig();
agentService.setControlConfig();
services.add(agentService);
} else {
debug(log,"[discoverServices] service='" + s.winServiceName + "' NOT runnig");
}
}
// creating Database services
try {
String obj = sqlServerMetricPrefix + ":Databases";
debug(log,"[discoverServices] obj='" + obj + "'");
String[] instances = Pdh.getInstances(obj);
debug(log,"[discoverServices] instances=" + Arrays.asList(instances));
for (String dbName : instances) {
if (!dbName.equals("_Total")) {
String path = dbsDisk.get(dbName);
String file = dbsFile.get(dbName);
ServiceResource service = new ServiceResource();
service.setType(this, DB_NAME);
service.setServiceName(dbName);
ConfigResponse cfg = new ConfigResponse();
cfg.setValue(MsSQLDetector.PROP_DB, dbName);
cfg.setValue("instance", instaceName);
if (path != null) {
cfg.setValue("disk", path);
cfg.setValue("master.file", file);
service.setProductConfig(cfg);
service.setMeasurementConfig();
services.add(service);
}
}
}
} catch (Win32Exception e) {
debug(log,"[discoverServices] Error getting Databases pdh data for '" + sqlServerServiceName + "': " + e.getMessage(), e);
}
return services;
}
private class ServiceInfo {
String winServiceName;
String type;
String metricsPrefix;
String serviceName;
public ServiceInfo(String winServiceName, String type, String metricsPrefix, String serviceName) {
this.winServiceName = winServiceName;
this.type = type;
this.metricsPrefix = metricsPrefix;
this.serviceName = serviceName;
}
}
protected static void debug(Log _log, String msg) {
if (_log.isDebugEnabled()) {
_log.debug(msg.replaceAll("(-P,? ?)([^ ,]+)", "$1******").replaceAll("(pass[^=]*=)(\\w*)", "$1******"));
}
}
protected static void debug(Log _log, String msg, Exception ex) {
if (_log.isDebugEnabled()) {
_log.debug(msg.replaceAll("(-P,? ?)([^ ,]+)", "$1******").replaceAll("(pass[^=]*=)(\\w*)", "$1******"), ex);
}
}
}