[ZBXNEXT-5649] consider replace json path with jsonpath plus Created: 2019 Dec 13 Updated: 2024 Apr 10 Resolved: 2020 Mar 18 |
|
Status: | Closed |
Project: | ZABBIX FEATURE REQUESTS |
Component/s: | Frontend (F), Proxy (P), Server (S) |
Affects Version/s: | 4.4.3 |
Fix Version/s: | 4.0.19rc1, 4.4.7rc1, 5.0.0alpha3, 5.0 (plan) |
Type: | Change Request | Priority: | Trivial |
Reporter: | gofree | Assignee: | Andris Zeila |
Resolution: | Fixed | Votes: | 3 |
Labels: | jsonpath, preprocessing | ||
Remaining Estimate: | Not Specified | ||
Time Spent: | Not Specified | ||
Original Estimate: | Not Specified |
Attachments: |
![]() ![]() ![]() |
Team: | |
Sprint: | Sprint 62 (Mar 2020) |
Story Points: | 1 |
Description |
Hi is it worth consideration to use json path plus in preproceesing instead of jsonpath. Couple extra and usefull functions againts pure jsonpath- see the page of the project. Example:
https://www.npmjs.com/package/jsonpath-plus |
Comments |
Comment by Craig Hopkins [ 2019 Dec 20 ] |
This was originally asked for in |
Comment by Alexei Vladishev [ 2020 Jan 31 ] |
It would be great to see some real use cases for this functionality. |
Comment by Andrew Boling [ 2020 Jan 31 ] |
I originally found this ticket looking to see if someone had already requested a "keys()" function or similar, but it appears that ~ from JSONPathPlus would also fulfill this requirement. Here is the jq syntax of a query I would like to run against Elasticsearch with Zabbix: $ curl -s -uzabbix 'https://127.0.0.1:443/_nodes/stats' | jq '.nodes | keys' Enter host password for user 'zabbix': [ "ItxolYAUQWSU1y3Z2jKpb5", "QCGrkuzxSG6V5CoK1fY9lp", "UJ5fRFnmREq_7y1oVkmz0P" ] Per https://stackoverflow.com/questions/46471516/get-keys-in-json, the equivalent JSONPathPlus syntax would be "$.nodes.*~". Rather than a rewrite of the current Zabbix JSONPath spec to align with JSONPathPlus, I would also be fine with new functions that emulate some of the missing functionalities: keys(), parent(), etc. |
Comment by Robert Grizzell [ 2020 Mar 02 ] |
In regards to a real-world use case, I'm attempting to do LLD on a list of services in Consul. Ideally, I wish to populate the {#CONSUL_SERVICE.NAME} macro with the name of the service. In the JSONPathPlus format, $.*~ should be sufficient enough to accomplish what I'm setting out to achieve. However, in the current Zabbix implementation, there doesn't appear to be a way to populate macros based on key's name rather than key's value. { "consul": [], "content": [ "2020.01.05", "golang" ], "login": [ "2019.11.02", "java" ], "mail": [ "2020.01.02", "golang" ] } $.*~ [ "consul" "content", "login", "mail" ] The current workaround is to use a UserParameter and middleware script to perform the API request and restructure the JSON payload into a format that Zabbix can use to populate those macros. I understand that this was the preferred approach in Zabbix versions prior to 4.2 however, I feel like this feature request would go a long way toward eliminating the need for middleware scripts for API calls altogether. |
Comment by Alexei Vladishev [ 2020 Mar 04 ] |
Will it be sufficient to implement support of "~" only? Any use cases for "^"? |
Comment by Robert Grizzell [ 2020 Mar 04 ] |
Using the JSON object from my previous example, there some advanced filtering that can be accomplished by implementing the parent ("^") functionality. Use Case: Get full object where golang exists in the array. $.[?(@ == "golang")]^ [ [ "2020.01.05", "golang" ], [ "2020.01.02", "golang" ] ] Use Case: Get the parent key's key name where golang exists in the array. $.[?(@ == "golang")]^~ [ "content", "mail" ] I only have a moderate understand of JSONPath, so I'm fulling willing to admit that there is likely to be a better way to accomplish both of the above outcomes via other methods. |
Comment by Andris Zeila [ 2020 Mar 05 ] |
Unfortunately relative paths @ in Zabbix jsonpath implementation can be applied only to objects/arrays - so expression ?(@ == "golang") would give no matches. However if name is always second member in the array, then you could use the following jsonpaths to achieve the same result: $.[?(@[1] == "golang")] $.[?(@[1] == "golang")]~ |
Comment by Andris Zeila [ 2020 Mar 16 ] |
Released
|
Comment by Alexander Vladishev [ 2020 Mar 18 ] |
Updated documentation: |
Comment by Craig Hopkins [ 2020 Mar 19 ] |
Which packaged version will be see this in for 5.0? I'm currently on 5.0.0~alpha3-1+buster which is the latest in the repo. |
Comment by Alexei Vladishev [ 2020 Mar 19 ] |
craigmcfly, it will be available in the next alpha of 5.0, also in stable 4.0.19 and 4.4.7. |
Comment by Craig Hopkins [ 2020 Apr 07 ] |
It's now accepting the JSON macro as valid but I don't seem to be getting much further forward. { "1": { "state": { "on": true, "bri": 254, "hue": 8597, "sat": 121, "effect": "none", "xy": [ 0.4452, 0.4068 ], "ct": 343, "alert": "select", "colormode": "xy", "mode": "homeautomation", "reachable": true }, "swupdate": { "state": "noupdates", "lastinstall": "2020-03-04T02:32:21" }, "type": "Extended color light", "name": "Office", "modelid": "LCT015", "manufacturername": "Signify Netherlands B.V.", "productname": "Hue color lamp", "capabilities": { "certified": true, "control": { "mindimlevel": 1000, "maxlumen": 806, "colorgamuttype": "C", "colorgamut": [ [ 0.6915, 0.3083 ], [ 0.17, 0.7 ], [ 0.1532, 0.0475 ] ], "ct": { "min": 153, "max": 500 } }, "streaming": { "renderer": true, "proxy": true } }, "config": { "archetype": "sultanbulb", "function": "mixed", "direction": "omnidirectional", "startup": { "mode": "powerfail", "configured": true } }, "uniqueid": "00:17:88:01:04:aa:aa:aa-aa", "swversion": "1.50.2_r30933", "swconfigid": "772B0E5E", "productid": "Philips-LCT015-1-A19ECLv5" } } Given the above object, I've set up a LLD macro as follows {#LIGHTID}$.*~ which should populate the macro with "1". I'm not getting any new items, though. I just get the error "Cannot find the "data" array in the received JSON object." |
Comment by Andris Zeila [ 2020 Apr 07 ] |
What version are you checking? If 4.0.x - then while '~' support was added to jsonpath, LLD still requires specific JSON format {"data":[<lld objects(rows)>]}. So this cannot be used for LLD in 4.0.x |
Comment by Craig Hopkins [ 2020 Apr 07 ] |
5.0 alpha 4 |
Comment by Andris Zeila [ 2020 Apr 07 ] |
Then you can easily test it from UI and see that it returns ["1"] Array is returned because * is used, so the path is indefinite. You can extract the "1" if you want with .first() function - $.@~.first() |
Comment by Craig Hopkins [ 2020 Apr 07 ] |
I don't see how to test LLD Macros from the UI. I'm not using preprocessing because I want the entire JSON object returned (I have about 20 lights to process, and I want the ID and name in one query). I don't understand what you mean by ".first()" in the LLD macros. That's not something I understand to be part of JSON. If this is part of Zabbix, can you steer me to the documentation? |
Comment by Andris Zeila [ 2020 Apr 07 ] |
I mean you can use preprocessing testing to play around with jsonpaths, so you see what it will return. As there are no jsonpath specification as such, it might have different results depending on implementation. So preprocessing testing will be easiest (while somewhat awkward) way to test it. Zabbix jsonpath implementation is described here. You may want to check supported functions and how the output value is generated.
|
Comment by Craig Hopkins [ 2020 Apr 07 ] |
You don't support JSONPath filtering in discovery preprocessing, as far as I can tell. |
Comment by Andris Zeila [ 2020 Apr 07 ] |
What do you mean with filtering? JSONpath preprocessing step is supported for discovery rules. |
Comment by Craig Hopkins [ 2020 Apr 07 ] |
Sorry, you're absolutely right. I found the JSONpath preprocessing step. So, if I do it as preprocessing, I get this: 1: JSONPath which is what I would expect, but as there's no macro for it to map that to (and I actually want to store more than just the ID as a macro), I need to manually define the macro. So, I moved the preprocessing rule to the Macros tab, and I get the error I mentioned above. |
Comment by Craig Hopkins [ 2020 Apr 07 ] |
Comment by Andris Zeila [ 2020 Apr 07 ] |
LLD works with arrays of objects, where an item (host etc) is discovered form one object. I understand that your json has format: { "1": {<data1>}, "2": {<data2>}, "3": {<data3>}, .... } So it needs to be converted to array which would contain unique references to the data. However I suspect the index->data relation is not unique - for example if data2 is removed, then index 2 will point at data3. One way how it could be done (assuming uniqueid is unique id):
Now to extract specific values create item prototype of dependent item type and set (3) as master item, adding jsonpath preprocessing with the value location, for example $.productid. You could skip the (3) part and extract values without intermediate result with jsonpath like $[?(@.uniqueid=="{#ID})].productid.first() but if you have a lot of values having intermediate object where you can directly access required values might be more simple and give better performance. Attached example host export - lights.xml Alternatively of course one could use javascript and convert the json to more LLD friendly format. |
Comment by Craig Hopkins [ 2020 Apr 07 ] |
Based on your summary, I don't understand why it doesn't work when I delete the #LIGHTNAME macro (i.e. just searching for $.*~). i.e. it's failing to gather a list of indexes. The whole point of expanding the jsonpath functionality was to allow for grabbing this top level info. |
Comment by Andris Zeila [ 2020 Apr 07 ] |
JSONpath is one thing, LLD is other thing. All '~' allows is to extract element name, it does not change how LLD is processed. For example now its possible to use [ {"1":<data1>} {"2":<data1>} {"3":<data1>} ] json for lld by defining lld macro {#ID} = $.*~.first() and then using $.{#ID}.propertyX to extract values. But as I understand in your case all data are packed in json object rather than an array of json objects. |
Comment by Craig Hopkins [ 2020 Apr 07 ] |
You keep referring to "first()" but I don't see this anywhere on https://www.zabbix.com/documentation/5.0/manual/discovery/low_level_discovery Can you elaborate, or steer me to the documentation I should be reading? |
Comment by Andris Zeila [ 2020 Apr 07 ] |
Oh, I linked it few hours ago:
first() is function to return first element of the array. When applying jsonpaths that might result in multiple matches (indefinite paths) all matched elements are returned in an array. Even if there is only one element, otherwise the result format would be dependent on input data, which would be bad. |
Comment by Craig Hopkins [ 2020 Apr 07 ] |
Thanks. I've read that through now. So in summary you haven't implemented LLD for $.*~ despite the request in https://support.zabbix.com/browse/ZBXNEXT-5649?focusedCommentId=411359&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-411359 |
Comment by Andris Zeila [ 2020 Apr 07 ] |
No, changing LLD logic is different thing and not something that can be added without careful consideration. If you need changes in LLD processing, please create a new ticket. Currently LLD works by iterating objects in array, there are few possible approaches:
Without more consideration it's hard to say which approach would be preferable. However such changes in functionality most probably would not be done in earlier versions. Regarding your json, is it real data or just an example? Because if it's real output from some monitoring, then I urge to check if the objects are really identified by the number of it's just an index in an array that can easily change when objects are added/removed. If it's second case, then your item history might get mixed up. |
Comment by Craig Hopkins [ 2020 Apr 07 ] |
Obviously it's disappointing that this wasn't included, as it was part of the use cases that Alexei asked for. The data I've provided you is real. Every subsequent query to the API is made using the ID that I'm trying to get from $.*~ |
Comment by dimir [ 2020 Apr 15 ] |
craigmcfly has created a separate one: ZBXNEXT-5890 |