
= Measurements API

You can use the Measurements API to retrieve collected values stored in RRD files, in Newts, and in other supported time series databases.

NOTE: Unless otherwise specified, all units of time are expressed in milliseconds.

== GET (reading data)

[caption=]
.Measurements API GET function
[options="autowidth"]
|===
| Resource  | Description

| /measurements/\{resourceid}/\{attribute}
| Retrieve the measurements for a single attribute.
|===

This table shows all supported query string parameters and their default values:

[options="autowidth"]
|===
| Parameter | Description   | Default

| start
| Timestamp.
If > `0`, the timestamp is relative to the UNIX epoch (January 1st 1970 00:00:00 AM). +
If < 0, the timestamp is relative to the `end` option (for example, default value is 4 hours ago).
| -14400000

| end
| Timestamp.
If \<= `0`, the effective value is the current timestamp.
| 0

| step
| Requested time interval between rows.
Actual step may differ.
| 300000

| maxrows
| When using the API to render a graph, this should be set to the graph's pixel width.
| 0

| aggregation
| Consolidation function used (`AVERAGE`, `MIN`, `MAX`).
The available options depend on the RRA definition.
| AVERAGE

| fallback-attribute
| Secondary attribute that is queried if the primary attribute does not exist.
| _Blank_
|===

=== Step sizes

The `step` parameter behaves differently depending on the time series strategy that your {page-component-title} instance uses.

When using persistence strategies based on RRD, the available step sizes are limited to those defined by the RRA when the file was created.
The effective step size used is one that covers the requested period and is closest to the requested step size.
For maximum accuracy, we recommend that you use a step size of `1`.

When using Newts, you can set the step size to an arbitrary number; this is because the aggregation is performed at request time.
To prevent large requests, {page-component-title} limits the step size to a maximum of 5 minutes (the default collection rate).
You can decrease this value by modifying `org.opennms.newts.query.minimum_step`.

=== cURL example

The following example demonstrates how you can use the GET function and its parameters with cURL.

This command retrieves CPU counter metrics for node 1, spanning the last two hours:

[source, bash]
curl -u admin:admin "http://127.0.0.1:8980/opennms/rest/measurements/node%5B1%5D.nodeSnmp%5B%5D/CpuRawUser?start=-7200000&maxrows=30&aggregation=AVERAGE"

Its response resembles the following snippet:

[source, xml]
----
<query-response end="1425588138256" start="1425580938256" step="300000">
  <columns>
    <values>159.5957271523179</values>
    <values>158.08531037527592</values>
    <values>158.45835584842285</values>
    ...
  </columns>
  <labels>CpuRawUser</labels>
  <metadata>
    <resources>
      ...
    </resources>
    <nodes>
      ...
    </nodes>
  </metadata>
  <timestamps>1425581100000</timestamps>
  <timestamps>1425581400000</timestamps>
  <timestamps>1425581700000</timestamps>
  ...
</query-response>
----

== POST (reading data)

[caption=]
.Measurements API POST function
[options="autowidth"]
|===
| Resource  | Description

| /measurements
| Retrieve the measurements for one or more attributes, possibly spanning multiple resources, with support for JEXL expressions.
|===

In this case, we use a POST function instead of a GET to retrieve the measurements.
This lets you perform complex queries that are difficult to express in a query string.

NOTE: You cannot use these requests to update or create new metrics.

=== cURL example

The following example demonstrates how you can use the POST function with cURL.

This command retrieves the bits in and bits out metrics for a specified interface.
Calculations are then performed on the bits out metric and only the derived values are returned:

[source, bash]
curl -X POST  -H "Accept: application/json" -H "Content-Type: application/json" -u admin:admin  -d @report.json  http://127.0.0.1:8980/opennms/rest/measurements

The derived values are dumped into `report.json`, and resemble the following snippets:

.`report.json` example
[source,json]
----
{
    "start": 1425563626316,
    "end": 1425585226316,
    "step": 10000,
    "maxrows": 1600,
    "source": [
        {
            "aggregation": "AVERAGE",
            "attribute": "ifHCInOctets",
            "label": "ifHCInOctets",
            "resourceId": "nodeSource[Servers:1424038123222].interfaceSnmp[eth0-04013f75f101]",
            "transient": "false" <1>
        },
        {
            "aggregation": "AVERAGE",
            "attribute": "ifHCOutOctets",
            "label": "ifHCOutOctets",
            "resourceId": "nodeSource[Servers:1424038123222].interfaceSnmp[eth0-04013f75f101]",
            "transient": "true" <2>
        }
    ],
    "expression": [
        {
            "label": "ifHCOutOctetsNeg",
            "value": "-1.0 * ifHCOutOctets",
            "transient": "false" <1>
        }
    ]
}
----
<1> Indicates that the attribute will be included in the response.
<2> Indicates that the attribute will not be included in the response; the query returns only the calculated expression result.

NOTE: All attributes referenced in an expression query must be requested individually by setting their `transient` value to `true` or `false`, as appropriate.
This ensures that the query response includes only the attributes that you want to enumerate.

.Response example
[source, json]
----
{
    "step": 300000,
    "start": 1425563626316,
    "end": 1425585226316,
    "timestamps": [
        1425563700000,
        1425564000000,
        1425564300000,
        ...
    ],
    "labels": [
        "ifHCInOctets", <1>
        "ifHCOutOctetsNeg" <1>
    ],
    "columns": [
        {
            "values": [
                139.94817275747508,
                199.0062569213732,
                162.6264894795127,
                ...
            ]
        },
        {
            "values": [
                -151.66179401993355,
                -214.7415503875969,
                -184.9012624584718,
                ...
            ]
        }
    ],
    "metadata": {
        "resources": [
            {
                "id": "nodeSource[Servers:1424038123222].interfaceSnmp[eth0-04013f75f101]",
                "label": "eth0-04013f75f101",
                "name": "eth0-04013f75f101",
                "parent-id": "nodeSource[Servers:1424038123222]",
                "node-id": 1
            },
            {
                "id": "nodeSource[Servers:1424038123222].interfaceSnmp[eth0-04013f75f101]",
                "label": "eth0-04013f75f101",
                "name": "eth0-04013f75f101",
                "parent-id": "nodeSource[Servers:1424038123222]",
                "node-id": 1
            }
            ],
            "nodes": [
            {
                "id": 1,
                "label": "Test Server",
                "foreign-source": "Servers",
                "foreign-id": "1424038123222"
            }
        ]
    }
}
----
<1> Attributes whose `transient` property is set to `false` in `report.json`.

== Advanced expressions

The JEXL 2.1.x library is used to parse expression strings.
This also lets you include Java objects and predefined functions in your expressions.

JEXL uses a context that {page-component-title} pre-populates with the results of your query.
Several constants and arrays are also predefined as references in the context:

[options="autowidth"]
|===
| Constant/Prefix   | Description

| __inf
| `Double.POSITIVE_INFINITY`

| __neg_inf
| `Double.NEGATIVE_INFINITY`

| NaN
| `Double.NaN`

| __E
| `java.lang.Math.E`

| __PI
| `java.lang.Math.PI`

| __diff_time
| Time span between the start and end of samples.

| __step
| Difference in time between subsequent values.

| __i
| Index into the samples array that the present calculation references.

| __AttributeName (where `AttributeName` is the searched-for attribute)
| This returns the complete `double[]` array of samples for `AttributeName`.
|===

{page-component-title} predefines a number of functions that you can use in expressions.
They are referenced by `namespace:function`, and they all return a Java double value:

[cols="2,3,1"]
|===
| Function  | Description   | Example

| jexl:evaluate("_formula"):
| Passes a string to the JEXL engine to evaluate as if it were entered as a normal expression. +
Like normal expressions, expressions evaluated through this function return a Java double value.
This lets you reference and evaluate a formula that has been stored in {page-component-title} as a string variable.
Because of this, you can define and store a per-node and per-value correction formula that can normalize samples from different sample sources.
| _None_

| math:
| References the `java.lang.Math` class.
| math:cos(20)

| strictmath:
| References the `java.lang.StrictMath` class.
| math:cos(20)

| fn:
| References the `org.opennms.netmgt.measurements.impl.SampleArrayFunctions` class.
This contains several functions that can reference previous samples in the time series.
| _None_

| fn:arrayNaN("sampleName", n)
| References sample _N_ in the defined sample series and replaces the _N_ samples before the start of the series with `NaN`.
| fn:arrayNaN("x", 5)

| fn:arrayZero("sampleName", n)
| References sample _N_ in the defined sample series and replaces the _N_ samples before the start of the series with `0`.
| fn:arrayZero("x", 5)

| fn:arrayFirst("sampleName", n)
| References sample _N_ in the defined sample series and replaces the _N_ samples before the start of the series with the first sample.
| fn:arrayFirst("x", 5)

| fn:arrayStart("sampleName", n, constant)
| References sample _N_ in the defined sample series and replaces the _N_ samples before the start of the series with a supplied constant.
| fn:arrayStart("x", 5, 10)
|===

With these additional expressions you can, for example, create a finite impulse response filter function such as `y = a * f(n) + b * f(n-1) + c * f(n-2)` using the following expression:

====
a * x + b * fn:arrayNaN("x", 1) + c * fn:arrayNaN("x", 2)
====

Here, `a`, `b`, and `c` are string constants, and `x` is a time series value.
