Forum Discussion

Jeff_Woeber's avatar
8 years ago

Use a Property Source to find dead devices

This is a PropertySource which runs with active discovery and adds the property auto.lmstatus to the device properties with the current Hoststatus value.  It does this using the REST API and It works great with Dynamic grouping, for example if you wanted to know which device in your portal were currently in a dead status you could create a dynamic group with the applies to of “auto.lmstatus=="dead".  One advantage to using a property source is if the device comes back on-line, Active Discovery will immediately run and change the property to "normal" removing the device from the group.  A copy of the PropertySource is at the bottom of the post.  

Let’s walk through the groovy script.  

Define the account information

This is polling the device properties.  I recommend setting this at the group level so they are inherited to the devices.  

an example of the properties would be

api.user = 5kCPqLgY4DGYP27uw2hc

api.pass = ye[$3y7)_4g6L6uH2TC72k{V6HBUf]Ys+9!vB)[9    *note, any property with .pass in the name will not have visible data.  

api.account = lmjeffwoeber

//Account Info
def accessId = hostProps.get("api.user");
def accessKey = hostProps.get("api.pass");
def account = hostProps.get("api.account");

Define the Query.  We just need the HostSatatus for the device the script is running on.  

def queryParams = '?fields=hostStatus&filter=displayName:'+hostName;
def resourcePath = "/device/devices"

Next we build the URL for the API.  

def url = "https://" + account + ".logicmonitor.com" + "/santaba/rest" + resourcePath + queryParams;

This next part builds the security and runs the API.  It can pretty much be copy\pasted into any groovy scripts that use the REST API.

//get current time
epoch = System.currentTimeMillis();

//calculate signature
requestVars = "GET" + epoch + resourcePath;

hmac = Mac.getInstance("HmacSHA256");
secret = new SecretKeySpec(accessKey.getBytes(), "HmacSHA256");
hmac.init(secret);
hmac_signed = Hex.encodeHexString(hmac.doFinal(requestVars.getBytes()));
signature = hmac_signed.bytes.encodeBase64();

// HTTP Get
CloseableHttpClient httpclient = HttpClients.createDefault();
httpGet = new HttpGet(url);
httpGet.addHeader("Authorization" , "LMv1 " + accessId + ":" + signature + ":" + epoch);
response = httpclient.execute(httpGet);
responseBody = EntityUtils.toString(response.getEntity());
code = response.getStatusLine().getStatusCode();

The API will return a JSON payload.  We use  the Groovy Slurper to transfer the payload to respose_obj were we can use the data.  
 

// user groovy slurper
json_slurper = new JsonSlurper();
response_obj = json_slurper.parseText(responseBody);

They JSON will look like
 

data=[total:1, items:[[hostStatus:dead]]

We can use the value with response_obj.data.items[0].hostStatus.value

Now we print the Key=Value for the property source

//print output
println "LMStatus=" +response_obj.data.items[0].hostStatus.value;
httpclient.close();

This will add the property source "auto.lmstatus" to the device

finally we return 0 do indicate a success.  

return (0);

This is how the PropertySource will appear on the device

JbHMCJc.jpg

 

Lastly, we can create Dynamic Groups based off the Hostatus.  For example use an applies to auto.lmstatus=="dead" to group all of the dead devices into one group, or auto.lmstatus=~"dead" to also include Dead-Collector

qLIbraN.jpg

 

LMStatus PropertySource

import org.apache.http.HttpEntity
import org.apache.http.client.methods.CloseableHttpResponse
import org.apache.http.client.methods.HttpGet
import org.apache.http.impl.client.CloseableHttpClient
import org.apache.http.impl.client.HttpClients
import org.apache.http.util.EntityUtils
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Hex;import com.santaba.agent.groovyapi.http.*;
import groovy.json.JsonSlurper;
	
def hostName = hostProps.get("system.displayname");

//Account Info
def accessId = hostProps.get("api.user");
def accessKey = hostProps.get("api.pass");
def account = hostProps.get("api.account");
data = ''
def queryParams = '?fields=hostStatus&filter=displayName:'+hostName;
def resourcePath = "/device/devices"
def url = "https://" + account + ".logicmonitor.com" + "/santaba/rest" + resourcePath + queryParams;

//get current time
epoch = System.currentTimeMillis();

//calculate signature
requestVars = "GET" + epoch + resourcePath;

hmac = Mac.getInstance("HmacSHA256");
secret = new SecretKeySpec(accessKey.getBytes(), "HmacSHA256");
hmac.init(secret);
hmac_signed = Hex.encodeHexString(hmac.doFinal(requestVars.getBytes()));
signature = hmac_signed.bytes.encodeBase64();

// HTTP Get
CloseableHttpClient httpclient = HttpClients.createDefault();
httpGet = new HttpGet(url);
httpGet.addHeader("Authorization" , "LMv1 " + accessId + ":" + signature + ":" + epoch);
response = httpclient.execute(httpGet);
responseBody = EntityUtils.toString(response.getEntity());
code = response.getStatusLine().getStatusCode();

// user groovy slurper
json_slurper = new JsonSlurper();
response_obj = json_slurper.parseText(responseBody);

//print output
println "LMStatus=" +response_obj.data.items[0].hostStatus.value;
httpclient.close();

return (0);

 

  • I believe this PS is made partially obsolete by the new system.hoststatus Device Property which was introduced in v149. 

    https://www.logicmonitor.com/support/v-149-release-notes

    However, in v153 that Property system.hoststatus was made restricted, preventing it from being used in any LogicModules.

    https://www.logicmonitor.com/support/v-153-release-notes

    (If you had already added it to the AppliesTo on a LogicModule before v153 it continues to work. You just can't make any additional edits to that module without removing that Property.)

    The system.hoststatus is still available and working on Dynamic Groups though.

  • Hi All

    One of the latest portal updates appears to have broken this propertysource.  The fix is to change this line

    println "LMStatus=" +response_obj.data.items[0].hostStatus.value

    to
    println "LMStatus=" +response_obj.data.items[0].hostStatus;
     
    No errors are any real concerns with the break, but the device will not report as dead without this fix.  
  • Anonymous's avatar
    Anonymous
    On 3/5/2018 at 8:14 AM, Jason Miller said:

    Thanks, got this to work.  I love it.  I added it to the Exchange:  6FXRA4

     

    Marked as private, cant download.

  • @Beau Breeden are you getting any property source applied? on a non-dead device you should still see a property source get attached. If it's working at all, you should see a property source added to each device "LMStatus=" saying alive or dead. If it's not working likely it's your API.user and API.pass keys that are incorrect and make sure you set the API.account as well to your subdomain. 

  • Anthony,

    I just applied added this as a PropertySource, specified a group of servers, and ran the script test. It succeeded, but applied the properties to 0 devices, even though there are roughly 500 devices, several of which are dead. Any suggestions to where I could be going wrong?

  • @Tom Lasswell no, active discovery does not run when a device's alert status changes (this could potentially be every few seconds, as various metrics went into and out of alert conditions).

  • This is great, just implemented. 

    Using for another feature as well in a dynamic group. We have at several times where we'll take major site outages that have 100+ devices, the collectors go a little bonkers with the failed task rates, so we can disable all the datasources and any existing alerts when the device goes dead. Added feature :)/emoticons/smile@2x.png 2x" title=":)" width="20"> 

    I noticed you're using the hostStatus, maybe we could also use the alertStatus as well, break it up, and say what the highest state of alert the device is as well? Does active discovery run when a device changes alert state as well? Or just when it goes from dead to normal? 

  • @Jeff.Woeber that's very cool!

    One minor tweak, the 'def hostName' line:

    def hostName = hostProps.get("system.displayname");

    ...should be:

    def hostName = java.net.URLEncoder.encode(hostProps.get("system.displayname"));

    ...to cope with spaces and other non-URL-friendly characters that are permitted in device display names.

    :)/emoticons/smile@2x.png 2x" title=":)" width="20">