JSON Parser - Operators

In the JSON parser, we have several operators which need to be used at different levels.

Top-level operators:

  • _vars - allows you to define variable which can be used later on in the script. The parser will basically replace the vars throughout the script with the value you set. For example, this:

    _vars:
    root: $.blahblah

    Means "${root}" will be replaced with "$.blahblah" throughout the script.

  • _tags - device tags. There is also a _tags section under metric, but more on that below. The "_tags" section at the top level is used to define device tags, such as "os.version" or "model". For example:
    _tags:
    "os.name":
    _constant: "panos"
    "os.version":
    _value: "$.system.sw-version"
    "vendor":
    _constant: "paloaltonetworks"
    "model":
    _value: "$.system.model"
  • _metrics - only allowed in monitoring scripts. Metrics are defined under this section. Each metric is under its own "-" section. It looks like this:
    _metrics:
    -
    _value.double:
    _count: "$.entries[0:]"
    _tags:
    "im.name":
    _constant: "routes-usage"

    A few things to note:

    • In case no metrics were parsed (due to missing data or incorrect parsing) then the command will fail; that is, unless some dynamic variables were parsed by the command. 
      • By using _optional_metrics instead of _metrics, one could override this behavior and not fail the command when no metrics are parsed.
    • The "_value.*" operator is used to determine the type of metric (more on this below). The "_value" operator and the "_value.double|complex" are two very different things. The first is used to capture the contents of a specific JsonPath, whereas the second is a header of a section telling the parser what type of metric this is.

    • The "_tags" section here is not the same as the one at the root level (described above). Rather, these are tags to attach to the metric.

Value-level operators:

Metrics can be either DOUBLE (a number) or COMPLEX (essentially a JSON structure):

  • _value.double - this is what you use for a DOUBLE metric. Underneath it you should simply say what the value is. For example:
    _value.double:
        _constant: "1"

    Which would set the value to "1". Of course, that's not terribly useful so you'll rarely see "_constant" used for _value.double. Instead you'll see one of the other operators listed further below.

    Default type is "gauge", for counters you will have to add a special tag called im.dsType:

    _tags:
    im.dsType:
        _constant: "counter"
     
  • _value.complex - this is for a COMPLEX metric. There are two JSON structures for complex metrics - one is a simple value such as this:
    {"value": "myval"} — note that the key is always "value" in this case

    The other is a JSON array, like this:
    [ {"key1": "someval", "key2": "someotherval"} , {"key1": "anotherval", "key2": "anotherotherval"} ]
    NOTE: there are two items in the above array. In each, there are two keys, each key has a value. The keys MUST match in the different items - a given item cannot have more or less, or different, keys compared to the others in the array. 

    To make the JSON parser to generate a JSON array for the complex metric, you must use the _groups operator (described below) as well as "_value.complex-array" operator (see more below).

When grabbing a value (for the double metric, for a temporary variable (discussed further below) or for a value in a JSON structure), you can use these operators: 

  • _value - gets the content of the value which matches the JsonPath. It takes the element we're currently in and extracts just the text from it (without the JSON element). For example, the text of this:
    {"name": "marco"}
    Is simply "marco".
  • _count - provide this operator with a JsonPath and it will return the number of elements it found matching the JsonPath.
  • _constant - refers to a string. The string to the right of this operator will be used as is, no JsonPath evaluation or calculation of any kind.

In order to iterate over multiple elements which match a certain JsonPath (for example, a list of users, or a list of disks), use the _groups section. For example:

_metrics:
    -
        _groups:
            $.jobs[0:]:                    # This will iterate over an array of elements under "jobs".
                _value.complex:
                    id:
                        _value: "id"
                    status:
                        _value: "status"
                    result:
                        _value: "result"
                    details:
                        _value: "details"
                _tags:
                    "im.name":
                        _constant: "jobs"
          _value: complex-array			   # Tells the parser to treat this as a JSON array - multiple objects, each has the id, status, result and details keys

Lastly, there's the _transform block. This allows you to translate the contents you've pulled from the XML to the format the metrics need. You use AWK to do this. For example:

Transform with double metric, no groups
_metrics:
    -
        _tags:
            "im.name":
                _constant: "uptime-seconds"
            "live-config":
               _constant: "true"
            "display-name":
                _constant: "Uptime"
            "im.dstype.displayType":
                _constant: "seconds"
        _temp:
            "uptime":
                _value: $.system.uptime
        _transform:
            _value.double: | 
               {
                   # 230 days, 16:57:34
                split("${temp.uptime}", vals, " ")
                if (arraylen(vals) == 3  && vals[2] == "days,") {
                    # 230 days, 16:57:34
                    days = vals[1]
                    split(vals[3], timevals, ":")
                    hours = timevals[1]
                    minutes = timevals[2]
                    seconds = timevals[3]
                    uptime = (days * 3600 * 24) + (hours * 3600) + (minutes * 60) + seconds
                    print uptime
                }
               }
Transform with double metrics, with groups
_metrics:
    -
        _groups:
            $.config.ntp[0:]:
                _temp:
                    status: 
                        _value: "status"
                _tags:
                    name:
                        _value: "name"
                    "im.name":
                        _constant: "ntp-server-state"
                    "live-config":
                       _constant: "true"
                    "display-name":
                        _constant: "NTP Servers - State"
                    "im.dstype.displayType":
                        _constant: "state"
                    "im.identity-tags":
                        _constant: "name"
        _transform:
            _value.double: |
                {
                    if ("${temp.status}" == "synched") {print "1.0"} else {print "0.0"}
                }