Solved

Is there a way to set custom property values from your groovy script in your datasource?



Show first post

70 replies

Userlevel 7
Badge +18

Yeah, that creates a string. In your working example, you create a map, not a string. Remove the single quotes from around the headers when you define it.

Userlevel 2
Badge +2

Well now i feel dumb.  it was simple as dropping the ‘’

Userlevel 7
Badge +18

Well now i feel dumb.  it was simple as dropping the ‘’

Don’t beat yourself up. Your mind was obviously working on the more difficult problems that you’ve still to surmount.

Userlevel 2
Badge +2

Can I pick your brain then on the proper syntax for the payload / body i would use for patching

Userlevel 7
Badge +18

FWIW: I just had a similar boneheaded moment with LM Support. I had an LM Logs pipeline alert setup, but it wasn’t triggering the alert because I had left it disabled.

Aaaaanyway, the body would simply be the stuff you want to update. Doing a PATCH to:

{{url}}/device/devices/:deviceid/devicedatasources/:hdsid/instances/:instanceid?opType=replace

The colons in my url are for Postman path variables. If you put the above URL in your postman, you should see it pop some additional fields into your Params tab. You can then put in the IDs without changing your url.

With a body like this:

{
"customProperties": [{"name": "test.property", "value": "testvalue"}]
}

I was able to patch my instance. If you wanted multiple properties patched:

{
"customProperties": [
{"name": "test.property", "value": "testvalue"},
{"name": "test.property2", "value": "testvalue2"}
]
}

These are obviously the JSON equivalents of the Groovy map you might be using to construct the json.

Userlevel 2
Badge +2

 

These are obviously the JSON equivalents of the Groovy map you might be using to construct the json.

Maybe that’s my hang up. I’ve tried every which way of json and groovy mapping i can think of and i still get this error

 

The script failed, elapsed time: 0 seconds - No signature of method: com.santaba.agent.groovyapi.http.Client.patch() is applicable for argument types: (org.codehaus.groovy.runtime.GStringImpl, groovy.json.internal.LazyMap, java.util.LinkedHashMap) values: [https://micoresolutions.logicmonitor.com/santaba/rest/device/devices/26/devicedatasources/3065/instances/69893279?patchFields=description&opType=replace, ...]Possible solutions: wait(), any(), each(groovy.lang.Closure)groovy.lang.MissingMethodException: No signature of method: com.santaba.agent.groovyapi.http.Client.patch() is applicable for argument types: (org.codehaus.groovy.runtime.GStringImpl, groovy.json.internal.LazyMap, java.util.LinkedHashMap) values: [https://micoresolutions.logicmonitor.com/santaba/rest/device/devices/26/devicedatasources/3065/instances/69893279?patchFields=description&opType=replace, ...]Possible solutions: wait(), any(), each(groovy.lang.Closure)	at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:71)	at org.codehaus.groovy.runtime.callsite.PojoMetaClassSite.call(PojoMetaClassSite.java:48)	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:144)	at Script190.run(Script190.groovy:25)

 

 

Userlevel 7
Badge +18

I can’t remember what type of object should be passed in with the patch, but it seems like it wants a map. Try not converting it to json first.

Userlevel 2
Badge +2

def payload = [name: "description",  value: "New York"]

 

I believe I tried using a map, assuming this is the correct way to do so.  Same result sadly.

 

Appreciate all the help btw.

Userlevel 7
Badge +18

Well, your payload needs adjusting. Needs to be a list of maps:

def payload = [[name:"description", value:"New York"]]

This is what the .patch() method got as its arguments:

(org.codehaus.groovy.runtime.GStringImpl, groovy.json.internal.LazyMap, java.util.LinkedHashMap)

It would seem that the second argument (which is your payload?) is a LazyMap but needs to be json. The error message gives a ton of useless information, but doesn’t get far enough to tell you what type of object needs to be passed in as the payload. I’m guessing string. Here’s how you convert an array/map to json:

import groovy.json.*
payload = [["name": "description", "value": "New York"]]
jsonPayload = JsonOutput.toJson(payload)
println(jsonPayload.getClass())

 

Userlevel 2
Badge +2

I think groovy just doesn’t like patch at all.  I’m gonna try figuring out a way instead to make put work.

Userlevel 7
Badge +18

Remember, the http client object is part of the santaba library (LM code), so IMO should be supported by LM. I’d push them in chat for help getting it working.

Userlevel 2
Badge +2

Good thinking.  In the mean time I’m gonna take a step back and figure out getting a proper auth signature in my script so i dont need to just copy and paste for testing.  It will need to be done regardless so while I wait for a response from them ill tackle this

 

Userlevel 7
Badge +18

Good thinking.  In the mean time I’m gonna take a step back and figure out getting a proper auth signature in my script so i dont need to just copy and paste for testing.  It will need to be done regardless so while I wait for a response from them ill tackle this

If your experience with the chat queue is like mine, you’ll have an hour or so to get it working while you’re waiting. How else would I have the time to be on the community as much as I am? 

 

Userlevel 2
Badge +2

Lol.  Have you ever generated the auth signature in groovy before.  I see lots of examples in other languages and that post about generating it using postman but nothing so much in groovy.  Although I could have sworn I saw one yesterday but for the life of me, I can’t find it now.

Userlevel 7
Badge +18
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import org.apache.commons.codec.binary.Hex

def generate_headers(id, key, path, data, verb) {
// Create encryption signature for authorization request
// data should be a json string (not a map)
try {
Long epoch_time = System.currentTimeMillis()
Mac hmac = Mac.getInstance("HmacSHA256")
hmac.init(new SecretKeySpec(key.getBytes(), "HmacSHA256"))
signature = Hex.encodeHexString(hmac.doFinal("${verb}${epoch_time}${data}${path}".getBytes())).bytes.encodeBase64()
return ["Authorization": "LMv1 $id:$signature:$epoch_time", "Content-Type": "application/json"]
} catch (Exception err) {println("ERROR: Unable to establish encryption for $path.\n${err.message}")}
}

I think this works. When I tested it with the wrong verb, i got a 401 (auth fail). When i tested it with the right verb, i got a 403 (permission denied) (because the api creds i used are RO). I think that means i did get authenticated, but my creds don’t have permission. I did finally find this, which would probably work too; but do me a favor and see if the function above works.

Userlevel 2
Badge +2

Sure I’ll give yours a shot first.  And that was the doc I saw yesterday!  Thanks for finding that one.  

Userlevel 2
Badge +2

I got hit with an auth failure as well.  I made sure its pulling the proper id and key.  I tried seeing if anything was amiss myself but im coming up blank  

 

Also I made sure data was in json string format and not a map or object or anything else.  

Userlevel 2
Badge +2

If you’ve got postman working, you’re doing well. That’s where most people trip up.

So, you need to construct this URL to do your patch:

/device/devices/{deviceId}/devicedatasources/{hdsId}/instances/{id}

To do that, you need three variables: deviceId, hdsId, and id. Sounds like you already have deviceId. 

To get hdsId (hardware datasource id, don’t ask why it’s called that, it doesn’t matter), you’ll need to do a GET on :

/device/devices/{deviceId}/devicedatasources?filter=dataSourceName:"Microsoft_SQLServer_GlobalPerformance"&fields=id,dataSourceName

The response will give you, as you’ve seen, the list of datasources on that device, but with the filter specified above, you should only get a response if there’s been an instance discovered on the device whose device ID is {deviceId}. If there’s not one there, you have chosen the id of a device that doesn’t have an instance for that datasource.

From there, you can use the id from the response (which is different for every device, yay!) to get the list of instances that you will be attempting to patch. Do a GET on:

/device/devices/{deviceId}/devicedatasources/{hdsId}/instances

The id shown in the response for each instance is the id that should go in the PATCH url.

Also, when you go to do your patch, there’s a query parameter: opType that will affect how your patch works, especially when it comes to properties. The swagger docs don’t even tell you what the possible values are much less what they do, so here it is:

Define custom properties for this device. Each property needs to have a name and a value. To add or update just one or a few device properties in the customProperties object, but not all of them, you’ll need to additionally use the opType query parameter. The opType query parameter can be set to add, refresh or replace. opType=add indicates that the properties included in the payload will be added, but all existing properties will remain the same. opType=replace indicates that the properties included in the request payload will be added if they don’t already exist, or updated if they do already exist, but all other existing properties will remain the same. opType=refresh indicates that the properties will be replaced with those included in the request payload.

So, the possible values are add, replace, and refresh. 

Add - You specify the properties you want to add. Existing properties are untouched. New properties are added. If your payload has a property that already exists, that property isn’t modified.

Replace - You specify the properties you want to update. New properties are added. If your payload has a property that already exists, that property is updated to match your payload.

Refresh - This is the nuclear option which will delete all properties and replace them with the properties in your payload.

You’d probably want to do replace.

 

Hey there, I’m circling back to this.  I wonder why When I run the get that finds the datasouce id.  

 

This part

 

/device/devices/{deviceId}/devicedatasources?filter=dataSourceName:"Microsoft_SQLServer_GlobalPerformance"&fields=id,dataSourceName

 

 

I come up blank.  I found the datasource ID before in a very roundabout way but i realize if I want this script to work for any device, I’ll need to automate it a little more.  

 

When I run a get on all datasources, it’s not even listed there.  Is there a logic monitor setting I need to enable to make the datasource visible to the API?

Userlevel 2
Badge +2

As a brief follow up.  I made a clone of the datasource to see if that one would show up with this get

 

{{url}}/device/devices/{id}/devicedatasources/

 

Turns out with device “A” I did fine my clone in the list

however device “B” does not show it. 

 

I verified both devices had the datasource in question by checking my resources tab in the UI

 

Userlevel 2
Badge +2

/device/devices/{deviceId}/devicedatasources?filter=dataSourceName:Microsoft_SQLServer_GlobalPerformance&fields=id,dataSourceName

 

Taking the quotes off of the datasource name value fixed it for me in postman.  now to test in logic mon

Userlevel 7
Badge +18

Be careful with that. Postman corrects errors like encoding spaces and quote marks. In groovy, you’ll need to escape the double quotes.

/device/devices/{deviceId}/devicedatasources?filter=dataSourceName:\”Microsoft_SQLServer_GlobalPerformance\”&fields=id,dataSourceName

When all else fails, output your URL after creating it in groovy to make sure it looks right.

Userlevel 7
Badge +18

FWIW: i had that happen once to me last week. In the time it took me to verify the DS was indeed on that device and I came back to postman, the same request resulted in data.

Userlevel 2
Badge +2

Now I’m getting an authentication failed when i add in the filter elements.  Why is groovy so mean to me? LOL

Userlevel 2
Badge +2

I got it.  I’m using Apache for what it’s worth to putt the request.  When you make a request with parameters using it you have to use 

import org.apache.http.client.utils.URIBuilder

 

Then Build the URI like so

 

URI uri = new URIBuilder(httpGet.getURI())

      .addParameter("fields", "id")

      .addParameter("filter", "dataSourceName:Microsoft_SQLServer_GlobalPerformance")

      .build();

    httpGet.setURI(uri);

 

I can post the whole code if you want to see.

Userlevel 7
Badge +18

Now I’m getting an authentication failed when i add in the filter elements.  Why is groovy so mean to me? LOL

It’s in Groovy’s nature. It’s why it’s not a very popular language outside very specific circles.

Reply