Forum Discussion

Michael_Baker's avatar
3 years ago

MTR path monitoring

I have not published this given the security implications running something like this... Would love some help to try reduce the risks

Provided the collector is running Linux (CentOS has only been tested) and the host has mtr installed this will monitor the path out to your LogicMonitor account along with the Latency, the path is stored as a Instance Level property on the device look at the updateState code... Every time this runs it compares the path and the previous path from the instance level property. If the path changes it updates another instance property  named alert_message with the old vs new path so you can put it in your favourite tool to see the diff, if you set the datapoints to alert you will get both old and new path's in the alert messages you receive.

You can also map the alerts to spikes in latency ect.

If anyone has a nicer way of managing all this please do!!! 

<?xml version="1.0" encoding="UTF-8" ?>
<feed  version="1.0" hasPendingRequests="false" >
  <company></company>
  <status>200</status>
  <errmsg>OK</errmsg>
  <interval>0</interval>
    <entry type="predatasource">
        <version>1631754840</version>
        <name>MTR to LogicMonitor</name>
        <displayedas>MTR to LogicMonitor</displayedas>
        <description>This Datasource will only work on Linux currently tested with CentOS 8 only, if you want to add this to a collecotr you will need to add a property to the instance mtr.monitoring</description>
        <collector>script</collector>
        <hasMultiInstances>false</hasMultiInstances>
        <schedule>180</schedule>
        <appliesTo>isCollectorDevice() &#38;&#38; getPropValue(&#34;mtr.monitoring&#34;)</appliesTo>
        <wildcardauto>false</wildcardauto>
        <wildcardpersist>false</wildcardpersist>
        <wildcardlinuxscript></wildcardlinuxscript>
        <wildcardlinuxcmdline></wildcardlinuxcmdline>
        <wildcardwinscript></wildcardwinscript>
        <wildcardwincmdline></wildcardwincmdline>
        <wildcardgroovyscript></wildcardgroovyscript>
        <wildcardschedule>1440</wildcardschedule>
        <wildcarddisable>false</wildcarddisable>
        <wildcarddeleteinactive>false</wildcarddeleteinactive>
        <agdmethod>none</agdmethod>
        <agdparams></agdparams>
        <group>Custom Monitoring</group>
        <tags></tags>
        <technology></technology>
        <adlist><![CDATA[{"agdmethod":"none","agdparams":"","id":0,"filters":[],"params":{}}]]></adlist>
        <schemaVersion>2</schemaVersion>
        <dataSourceType>1</dataSourceType>
        <attributes>
        <attribute>
            <name>scripttype</name>
            <value>embed</value>
            <comment></comment>
        </attribute>
        <attribute>
            <name>scriptgroovy</name>
            <value>import com.santaba.agent.groovyapi.expect.Expect;
import com.santaba.agent.groovyapi.snmp.Snmp;
import com.santaba.agent.groovyapi.http.*;
import com.santaba.agent.groovyapi.jmx.*;
import org.xbill.DNS.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import groovy.json.*
import org.apache.commons.codec.binary.Hex
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import com.santaba.agent.util.Settings
import java.security.MessageDigest
import com.santaba.agent.live.LiveHostSet
import org.apache.http.client.methods.*
import org.apache.http.entity.ContentType
import org.apache.http.entity.StringEntity
import org.apache.http.impl.client.CloseableHttpClient
import org.apache.http.impl.client.HttpClients
import org.apache.http.client.config.RequestConfig
import org.apache.http.HttpHost
import org.apache.http.util.EntityUtils
import java.net.URLEncoder

proxy_host = hostProps.get(&#34;proxy.host&#34;)?hostProps.get(&#34;proxy.host&#34;):null
proxy_port = hostProps.get(&#34;proxy.port&#34;)?hostProps.get(&#34;proxy.port&#34;):null
String apiId = hostProps.get(&#34;lmaccess.id&#34;)?:hostProps.get(&#34;apiaccessid.key&#34;)
String apiKey = hostProps.get(&#34;lmaccess.key&#34;)?:hostProps.get(&#34;apiaccesskey.key&#34;)
def portalName = hostProps.get(&#34;lmaccount&#34;)?:Settings.getSetting(Settings.AGENT_COMPANY)

if (proxy_host) {
  System.getProperties().put(&#34;proxySet&#34;, &#34;true&#34;);
  System.getProperties().put(&#34;proxyHost&#34;, proxy_host);
  System.getProperties().put(&#34;proxyPort&#34;, proxy_port);
}

def currentPath = instanceProps.get(&#39;mtr.path&#39;)
def address = &#34;${portalName}.logicmonitor.com&#34;;

proc = (&#39;mtr --csv &#39; + address).execute().text;
ArrayList&#60;String&#62; path = new ArrayList&#60;String&#62;();
if(proc)
{
    i = 0
	proc.eachLine
	{
		try
		{
		  hops = it.split(&#39;,&#39;)
          if(i != 0) {
          path.add(hops[5]);
          i += 1;
          }
          else {
          i += 1;
          }
        }
		catch(e)
		{
			hopSteps += &#39;Error: &#39; + e + &#39;\n&#39;;
		}
	}

    fullPath = path.join(&#39;,&#39;)
    if(fullPath != currentPath) {
        updateState(fullPath, instanceProps.get(&#34;system.instanceid&#34;), hostProps.get(&#34;system.deviceId&#34;), instanceProps.get(&#34;instance&#34;), portalName, apiId, apiKey, &#39;mtr.path&#39;)
        updateState(&#34;Original Path:${currentPath} New Path: ${fullPath}&#34;, instanceProps.get(&#34;system.instanceid&#34;), hostProps.get(&#34;system.deviceId&#34;), instanceProps.get(&#34;instance&#34;), portalName, apiId, apiKey, &#39;alert_message&#39;)
        println &#34;path_changed=1&#34;
    }  else {
        println &#34;path_changed=0&#34;     
     }
     icmp_time = (&#34;ping ${address} -c1&#34;).execute().text =~ /time=(\d+)/;
     println &#34;icmp_time=${icmp_time[0][1]}&#34;
}
else
{
	println &#39;UNKNOWN issue&#39;;
}

return 0;

// internal stuff
def updateState(message, instanceId, deviceId, dataSourceName, portalName, apiId, apiKey, field) {
    datasourceId = getDataSourceId(deviceId, dataSourceName, portalName, apiId, apiKey)
    instance_data = &#34;{\&#34;customProperties\&#34;:[{\&#34;name\&#34;:\&#34;mtr.path\&#34;,\&#34;value\&#34;:\&#34;${message}\&#34;}]}&#34;
    old_data = apiGetV2(portalName, apiId, apiKey, &#34;/device/devices/${deviceId}/devicedatasources/${datasourceId[&#39;ds_id&#39;]}/instances/${datasourceId[&#39;instance_id&#39;]}&#34;)
    old_data[&#39;description&#39;] = message
    old_data[&#39;customProperties&#39;] &#60;&#60; [name: field, value: message]
    json_data = groovy.json.JsonOutput.toJson(old_data)
    response = rawPutV2(&#34;PUT&#34;,apiId,apiKey,portalName,&#34;/device/devices/${deviceId}/devicedatasources/${datasourceId[&#39;ds_id&#39;]}/instances/${datasourceId[&#39;instance_id&#39;]}&#34;,&#34;&#34;,json_data)
}

def getDataSourceId(deviceId, dataSourceName, portalName,apiId,apiKey) {
     //pp(do_request(&#39;GET&#39;, &#39;/setting/datasources?filter=name:&#34;Crestone custom test&#34;&#39;))
     def args = [filter:&#34;dataSourceName:\&#34;${dataSourceName}\&#34;&#34;]
     data = apiGetV2(portalName, apiId, apiKey, &#34;/device/devices/${deviceId}/devicedatasources&#34;, args)
     instance_data = apiGetV2(portalName, apiId, apiKey, &#34;/device/devices/${deviceId}/devicedatasources/${data[&#39;items&#39;][0][&#39;id&#39;]}/instances&#34;)
     mapping_data = [instance_id: instance_data[&#39;items&#39;][0][&#39;id&#39;], ds_id: instance_data[&#39;items&#39;][0][&#39;deviceDataSourceId&#39;]]
     return mapping_data
}

def apiGetV2(portalName, apiId, apiKey, endPoint, Map args=[:]) {
    def request = rawGetV2(portalName, apiId, apiKey, endPoint, args)
    if (request.getResponseCode() == 200) {
        def payload = new JsonSlurper().parseText(request.content.text)
        return payload
    }
    else {
        throw new Exception(&#34;Server return HTTP code ${request.getResponseCode()}&#34;)
    }
}
def rawGetV2(portalName, apiId, apiKey, endPoint, Map args=[:]) {
    def auth = generateAuth(&#39;GET&#39;, apiId, apiKey, endPoint)
    def headers = [&#34;Authorization&#34;: auth, &#34;Content-Type&#34;: &#34;application/json&#34;, &#34;X-Version&#34;:&#34;2&#34;, &#34;External-User&#34;:&#34;true&#34;]
    def url = &#34;https://${portalName}.logicmonitor.com/santaba/rest${endPoint}&#34;

    if (args) {
        def encodedArgs = []
        args.each{ k,v -&#62;
            encodedArgs &#60;&#60; &#34;${k}=${java.net.URLEncoder.encode(v.toString(), &#34;UTF-8&#34;)}&#34;
        }
        url += &#34;?${encodedArgs.join(&#39;&#38;&#39;)}&#34;
    }
    def request = url.toURL().openConnection()
    headers.each{ k,v -&#62;
        request.addRequestProperty(k, v)
    }

    return request
}

def rawPutV2(_verb, _accessId, _accessKey, _account, _resourcePath, _queryParameters, _data){
  responseDict = [:]
  url = &#39;https://&#39; + _account + &#39;.logicmonitor.com&#39; + &#39;/santaba/rest&#39; + _resourcePath + _queryParameters
  StringEntity entity = new StringEntity(_data)
  epoch = System.currentTimeMillis()
  requestVars = _verb + epoch + _data + _resourcePath
  hmac = Mac.getInstance(&#39;HmacSHA256&#39;)
  secret = new SecretKeySpec(_accessKey.getBytes(), &#39;HmacSHA256&#39;)
  hmac.init(secret)
  hmac_signed = Hex.encodeHexString(hmac.doFinal(requestVars.getBytes()))
  signature = hmac_signed.bytes.encodeBase64()
  CloseableHttpClient httpclient = HttpClients.createDefault()
  http_request = new HttpPut(url)
  if (proxy_host) {
    RequestConfig requestConfig = RequestConfig.custom()
        .setProxy(new HttpHost(proxy_host, new Integer(proxy_port)))
        .build();
    http_request.setConfig(requestConfig);
  }
  http_request.addHeader(&#34;Authorization&#34;, &#34;LMv1 &#34; + _accessId + &#34;:&#34; + signature + &#34;:&#34; + epoch)
  http_request.setHeader(&#34;X-Version&#34;, &#34;2&#34;)
  http_request.setHeader(&#34;Accept&#34;, &#34;application/json&#34;)
  http_request.setHeader(&#34;Content-type&#34;, &#34;application/json&#34;)
  http_request.setEntity(entity)
  response = httpclient.execute(http_request)
  responseBody = EntityUtils.toString(response.getEntity())
  code = response.getStatusLine().getStatusCode()
  responseDict[&#39;code&#39;] = code ?: null
  responseDict[&#39;body&#39;] = responseBody ?: null
  return responseDict
}

static String generateAuth(method,id, key, path) {
    Long epoch_time = System.currentTimeMillis()
    Mac hmac = Mac.getInstance(&#34;HmacSHA256&#34;)
    hmac.init(new SecretKeySpec(key.getBytes(), &#34;HmacSHA256&#34;))
    def signature = Hex.encodeHexString(hmac.doFinal(&#34;${method}${epoch_time}${path}&#34;.getBytes())).bytes.encodeBase64()

    return &#34;LMv1 ${id}:${signature}:${epoch_time}&#34;
}</value>
            <comment></comment>
        </attribute>
        <attribute>
            <name>windowsscript</name>
            <value></value>
            <comment></comment>
        </attribute>
        <attribute>
            <name>linuxscript</name>
            <value></value>
            <comment></comment>
        </attribute>
        <attribute>
            <name>windowscmdline</name>
            <value></value>
            <comment></comment>
        </attribute>
        <attribute>
            <name>linuxcmdline</name>
            <value></value>
            <comment></comment>
        </attribute>
        <attribute>
            <name>properties</name>
            <value></value>
            <comment></comment>
        </attribute>
        </attributes>
        <datapoints>
        <datapoint>
            <name>path_changed</name>
            <dataType>7</dataType>
            <type>2</type>
            <postprocessormethod>namevalue</postprocessormethod>
            <postprocessorparam>path_changed</postprocessorparam>
            <usevalue>output</usevalue>
            <alertexpr></alertexpr>
            <alertmissing>1</alertmissing>
            <alertsubject>MTR path changes to your LogicMonitor account</alertsubject>
            <alertbody>MTR path has changed:

##alert_message##</alertbody>
            <enableanomalyalertsuppression></enableanomalyalertsuppression>
            <adadvsettingenabled>false</adadvsettingenabled>
            <warnadadvsetting></warnadadvsetting>
            <erroradadvsetting></erroradadvsetting>
            <criticaladadvsetting></criticaladadvsetting>
            <description></description>
            <maxvalue></maxvalue>
            <minvalue></minvalue>
            <userparam1></userparam1>
            <userparam2></userparam2>
            <userparam3></userparam3>
            <iscomposite>false</iscomposite>
            <rpn></rpn>
            <alertTransitionIval>0</alertTransitionIval>
            <alertClearTransitionIval>0</alertClearTransitionIval>
        </datapoint>
        <datapoint>
            <name>icmp_time</name>
            <dataType>7</dataType>
            <type>2</type>
            <postprocessormethod>namevalue</postprocessormethod>
            <postprocessorparam>icmp_time</postprocessorparam>
            <usevalue>output</usevalue>
            <alertexpr></alertexpr>
            <alertmissing>1</alertmissing>
            <alertsubject></alertsubject>
            <alertbody></alertbody>
            <enableanomalyalertsuppression></enableanomalyalertsuppression>
            <adadvsettingenabled>false</adadvsettingenabled>
            <warnadadvsetting></warnadadvsetting>
            <erroradadvsetting></erroradadvsetting>
            <criticaladadvsetting></criticaladadvsetting>
            <description></description>
            <maxvalue></maxvalue>
            <minvalue></minvalue>
            <userparam1></userparam1>
            <userparam2></userparam2>
            <userparam3></userparam3>
            <iscomposite>false</iscomposite>
            <rpn></rpn>
            <alertTransitionIval>0</alertTransitionIval>
            <alertClearTransitionIval>0</alertClearTransitionIval>
        </datapoint>
        </datapoints>
        <graphs>
        <graph>
            <name>Latency to LogicMonitor</name>
            <title>Latency to LogicMonitor</title>
            <verticallabel>ms</verticallabel>
            <rigid>false</rigid>
            <maxvalue>NaN</maxvalue>
            <minvalue>NaN</minvalue>
            <displayprio>1</displayprio>
            <timescale>1day</timescale>
            <base1024>false</base1024>
            <graphdatapoints>
        <graphdatapoint>
            <name>ICMPms</name>
            <datapointname>icmp_time</datapointname>
            <cf>1</cf>
        </graphdatapoint>
            </graphdatapoints>
            <graphvirtualdatapoints>
            </graphvirtualdatapoints>
            <graphdatas>
            <graphdata>
                <type>1</type>
                <legend>ICMPms</legend>
                <color>silver</color>
                <datapointname>ICMPms</datapointname>
                <isvirtualdatapoint>false</isvirtualdatapoint>
            </graphdata>
            </graphdatas>
        </graph>
        </graphs>
        <overviewgraphs>
        </overviewgraphs>
        <scripts>
        </scripts>
    </entry>
</feed>

 

No RepliesBe the first to reply