Forum Discussion

StephenP's avatar
4 years ago

SQL Replication Monitoring

Hi All, first post on the forums.

I'm trying to setup some monitoring of our MSSQL replication to try and keep a track and see when it fails or issues crop up.

I've tried to create a data source but am not 100% sure what i'm doing in the groovy space. I've grabbed a script that does a SQL query as a base, but i need to run a procedure (sp_replmonitorhelppublication) instead. For active discovery i'm not getting any instances.

What i have is below and would be great if anyone could poing me into the right direction as to where i've gone wrong?

In this case, mssl.MSSQLSERVER.replication_url looks like jdbc:sqlserver://server_name:1433;databasename=distribution;integratedSecurity=false;applicationIntent=ReadOnly (mainly because i didn't know how to specify the database in groovy)
 

import groovy.sql.Sql

// Get basic info
def hostname = hostProps.get("system.hostname")
def sqlInstances = hostProps.get("mssl.MSSQLSERVER.replication_url") ?: hostProps.get("auto.sql_server_instances")  // override sql instances with manual instances
Boolean debug = false

LMDebugPrint("***** Running System Jobs AD", debug)

// This statement will return nothing if qroups aren't defined.  Hard to return an error message since some named instances can be members of a group and others may not be.

def sqlQuery = """
    sp_replmonitorhelppublication
  """

LMDebugPrint("\tSQL Instances ${sqlInstances}", debug)
if (sqlInstances)
{
    sqlInstances.toString().tokenize(",").each
    { instanceName ->
        LMDebugPrint("\tInstance Name ${instanceName}", debug)
        def jdbcConnectionString = hostProps.get("mssql.${instanceName.trim()}.mssql_url") ?: hostProps.get("auto.${instanceName.trim()}.mssql_url")
        def user = hostProps.get("mssql.${instanceName.trim()}.user") ?: hostProps.get("jdbc.mssql.user") ?: ""
        def pass = hostProps.get("mssql.${instanceName.trim()}.pass") ?: hostProps.get("jdbc.mssql.pass") ?: ""
        LMDebugPrint("\tJDBC Connection String  ${jdbcConnectionString}", debug)
        // ensure we dont have any null's
        if (jdbcConnectionString)
        {
            def conn = attemptConnection(user, pass, jdbcConnectionString, debug)
            if (conn.status == "success")
            {
                def outputRecords = runQuery(sqlQuery, conn.connection, debug)
                if (outputRecords.status == 'success')
                {
                    outputRecords.data.each { oneRecord ->
                        name = oneRecord.publication.toString().replaceAll(/[:|\\|\s|=]+/,"_")
                        publication_id = oneRecord.publication_id
                        publication_type = oneRecord.publication_type
                        retention = oneRecord.retention
                        instanceName = instanceName.replaceAll(/[:|\\|\s|=]+/,"_")
                        wildvalue = instanceName + "||" + name
                        // Print it out with instance level properties.
                        println "${wildvalue}##${name} (${instanceName})######" +
                        "auto.sql_instance=${instanceName}&" +
                        "auto.publication_name=${name}&" +
                        "auto.publication_id=${publication_id}&" +
                        "auto.publication_type=${publication_type}&" +
                        "auto.retention=${retention}"
                    }
                }
            }
        }
    }
    return 0
}
else
{
    return 1
}


/**
 * Helper method to print out debug messages for troubleshooting purposes.
 * @param message
 * @param debug
 * @return
 */

def LMDebugPrint(message, Boolean debug = false)
{
    if (debug)
    {
        println(message.toString())
    }
}


/**
 * Helper method which handles creating a connection to the jdbc database
 * @returnArray is an array with a connection, status and any error messages an array.
 *          *connection = jdbc connection
 *          *status, success or fail
 *          *errors, if both connection types fail there will be 2 error messages.
 *
 */
def attemptConnection(String instanceUser, String instancePass, String instanceURL, Boolean debug = false)
{
    LMDebugPrint("**** Props to connect:", debug)
    LMDebugPrint("\tuser:$instanceUser", debug)
    LMDebugPrint("\tinstanceURL:$instanceURL", debug)
    def returnArray = [:]
    def errors = []
    def connComplete
    def db_connection

    try
    {
        // Connection creation thrown into Try/Catch block as to quickly capture any issues with initial connection.
        // Create a connection to the database.
        String driver = "com.microsoft.sqlserver.jdbc.SQLServerDriver"
        db_connection = Sql.newInstance(instanceURL, instanceUser, instancePass, driver)
        connComplete = true

    }
    catch (Exception e)
    {
        // Print out the exception and exit with 1.
        errors[0] = e.message
        LMDebugPrint("***** Attempt Connection error: ${e.message}", debug)
        connComplete = false
    }

    // populate the connection and any messages for the return array
    if (connComplete == true)
    {

        returnArray['connection'] = db_connection
        returnArray['status'] = 'success'
    }
    else
    {
        returnArray['status'] = 'failed'
        returnArray['errors'] = errors
        returnArray['connection'] = null
    }

    return returnArray

}


/*
Attempt to execute SQL command.
Returns an array.
    the first element is the query data
    second element is success or fail
    third element is the error message
 */

def runQuery(String sqlQuery, conn, debug = false)
{
    def returnArray = [:]
    LMDebugPrint("****Running Query****", debug)
    LMDebugPrint("\tQuery to run: $sqlQuery", debug)
    // query the Oracle DB.
    try
    {

        returnArray['data'] = conn.rows(sqlQuery)
        returnArray['status'] = 'success'
        returnArray['error'] = ''
    }
    catch (Exception e)
    {
        returnArray['error'] = e.message
        returnArray['status'] = 'failed'
        LMDebugPrint("\tSQL Query Error message: ${e.message}", debug)
    }
    LMDebugPrint("Data Returned: ${returnArray.data}", debug)
    return returnArray
}

  • Anonymous's avatar
    Anonymous

    If you can construct the connection string to get into the database and you can run the procedure as part of a query (not a SQL guy, so not sure how that works), you might be better off making a JDBC DataSource. That way you skip the coding altogether.

    As for AD failing, exactly what are you looking to see as instances on the device? Would you see each database because your procedure will output the replication status of each database? Or is it a server level thing? If it's a server level thing, you don't need active discovery at all. If you will be monitoring each db's replication status, then each DB would need to be discovered