10 Webservice engine

The Webservice engine obtains data by calling web services over HTTP. It supports two kinds of service, REST and SOAP, which can coexist in the same query file and the same monitor.

10.1 REST and SOAP#

Both kinds share the connection (base URL, TLS, authentication) and the way values are extracted: in both cases the response is turned into a JSON document and each item gives a path inside it. They differ in how the request is built and in the original format of the response:

REST (type="REST") SOAP (type="SOAP")
Request GET or POST to a URL. POST of a SOAP XML envelope with a method and its arguments.
Key parameters Can be sent in the URL (urlparameter) or used to choose an element of the response (path with <base>). Not used: the method arguments are fixed in the XML file.
Response JSON. SOAP XML, which Databbix converts into JSON (section 10.4.1).
status_code in <transform> HTTP status code of the response. Always 0; errors arrive as a Fault in the JSON itself.
Discoveries Yes. No.
Authentication HTTPAuth, RESTToken, JDE. Oasis (WS-Security) or HTTPAuth.

10.2 Configuration: the [Webservice] section#

Parameter Description
BaseURL Mandatory. Base URL. Query URLs are relative to it.
UserName, Password Credentials.
Credentials Additional credential of the form key=value (environment, role, method…), depending on the authentication engine. Can be repeated.
Authenticator Name of an authentication method defined in the XML files or, if none exists, of an authentication engine: HTTPAuth, JDE or Oasis (section 10.6).
Timeout Maximum wait time in milliseconds (default: 20000).
AuthPersistFile File where the authentication state (for example, the token) is saved between restarts.
VerifyCertificate Verify the server certificate (default: yes).
CertCAFile CA file used instead of the system certificate store.
ClientCertificate Client certificate (PEM) for TLS authentication.
ClientPrivKey Private key of the client certificate. Mandatory if ClientCertificate is used.
ClientPrivKeyPass Password of the private key, if encrypted.
DisableContent Encoding Disables Content-Encoding, for old servers (default: no).
[General]
Name = JDEPROD
Engine = Webservice
QueryFile = /etc/databbix/query-jde.xml

[Zabbix]
Server = zabbix.example.com
Port = 10051

[Data]
Module = JDE

[Webservice]
BaseURL = http://10.1.1.45:9888/restservice
UserName = monitor
Password = secret
Authenticator = JDE

10.3 REST queries#

<Queries engine="Webservice">

<query name="JDEEntStatus" module="JDE" type="REST" transport="GET" encode="Json">
    <url>entserverinstanceinfo</url>
    <parameter urlparameter="instanceName" order="1" type="STRING"/>
    <item name="ts.jde.enterprise.kerneljobs"
          path="entServerInstanceInfoDetail:[0]:kernelJobs" type="INT"/>
</query>

</Queries>
Element / attribute Description
type="REST" <query> attribute. Mandatory.
transport Mandatory. GET or POST.
encode Response format. Only Json is supported.
<url> Mandatory. Path relative to BaseURL.
<parameter urlparameter> Sends the key parameter in the URL, as ?name=value (section 10.3.2).
<base path> Array of the response that is iterated to find the element given by the key (section 10.3.3).
<parameter path> With <base>: field of each element compared with the key parameter.
<item path type> Path of the value inside the JSON (section 10.3.1) and type: INT or STRING. Both mandatory, except in built_in items.
<transform> Function that processes the response before the values are extracted (section 12.6).

Without <transform>, a response with an HTTP status other than 200 is an error and all the query’s items become not supported.

10.3.1 JSON paths#

A path says how to reach a value from the root of the JSON document. It is made of steps separated by colons (:):

Step Meaning
name Member name of an object.
[n] Element n of an array, starting at 0.
[] All the elements of an array. Only in <base> and in the <item> of a discovery.
* All the members of an object. Only in <base> and in the <item> of a discovery.

An item’s path must end at a simple value (text, number, boolean or null). Given this document:

{
  "server":   { "name": "ent01", "state": "RUNNING", "up": true },
  "jobs": [
    { "id": 1, "name": "backup", "status": "OK",     "duration": 120 },
    { "id": 2, "name": "purge",  "status": "FAILED", "duration": 30 }
  ],
  "counters": { "cpu": 12.5, "memory": 2048 },
  "note": null
}

Paths and values obtained

path type Value
server:name STRING ent01
server:up INT 1
server:up STRING true
jobs:[1]:status STRING FAILED
jobs:[0]:duration INT 120
counters:cpu STRING 12.5
note STRING (empty string)
server:host STRING not supported: the member does not exist
jobs:[5]:status STRING not supported: the array has 2 elements
server STRING error: not a simple value

In the not supported cases, if the item has a default attribute, that value is sent. With type="INT", booleans are turned into 1 and 0.

Note.  Member names cannot contain colons, because the colon is the step separator. If the service uses them, restructure the response with a transformation (section 12.6).

10.3.2 Parameters in the URL#

Each <parameter> with urlparameter adds to the URL a parameter with that name and the key value. The query runs once for each distinct key:

<query name="JDEInstanceState" module="JDE" type="REST" transport="GET" encode="Json">
    <url>instancestate</url>
    <parameter urlparameter="instanceName" order="1" type="STRING"/>
    <item name="ts.jde.instance.state" path="instanceState" type="STRING"/>
</query>

Calls and values obtained

Key URL called Value
ts.jde.instance.state[ENT1] …/instancestate?instanceName=ENT1 RUNNING
ts.jde.instance.state[WEB1] …/instancestate?instanceName=WEB1 STOPPED

On each call the service answers, for example, {"instanceState":"RUNNING"}, and the path instanceState extracts the value.

10.3.3 Filtering the response#

When the service returns the list of all objects at once, it is more efficient to make a single call and pick the element in the response, just like a Database FILTER query. To do so:

  • <base path="…"> gives the array to iterate (its path ends in []);

  • each <parameter path="…"> gives a field of each element that is compared with a key parameter;

  • item paths are relative to the selected element.

<query name="LocalJobs" module="API" type="REST" transport="GET" encode="Json">
    <url>status</url>
    <base path="jobs:[]"/>
    <parameter path="name" order="1" type="STRING"/>
    <item name="local.api.job.status"   path="status"   type="STRING" default="UNKNOWN"/>
    <item name="local.api.job.duration" path="duration" type="INT"/>
</query>

With the document of the previous section as the response:

Values obtained

Key Selected element Value
local.api.job.status[backup] jobs[0] OK
local.api.job.duration[backup] jobs[0] 120
local.api.job.status[purge] jobs[1] FAILED
local.api.job.status[reindex] none UNKNOWN (default)
local.api.job.duration[reindex] none not supported

The five keys are resolved with a single call to the service. With type="INT" in the <parameter>, the comparison is numeric (for example, parameter path="id" and the key …[2]).

10.3.4 REST discoveries#

The path of a discovery’s <item> iterates over an array ([]) or over the members of an object (*). Each element iterated is a discovered entity, and the <field> paths are relative to it:

<query name="LocalJobDiscovery" module="API" type="REST"
       transport="GET" encode="Json" discovery="yes">
    <url>status</url>
    <item name="local.api.job.discovery" path="jobs:[]"/>
    <field name="JOB"    path="name"/>
    <field name="JOBID"  path="id"/>
</query>

With the document of section 10.3.1, the item value is:

{"data":[ {"{#JOB}":"backup", "{#JOBID}":"1"},
          {"{#JOB}":"purge",  "{#JOBID}":"2"} ]}

Nested iteration#

A path can have several iteration steps. With this response:

{ "clusters": [
    { "name": "A", "nodes": [ {"host": "a1"}, {"host": "a2"} ] },
    { "name": "B", "nodes": [ {"host": "b1"} ] }
] }

Item path and discovered entities

Item path Entities (and available field path="…")
clusters:[] 2: clusters A and B (name, nodes:[0]:host…)
clusters:[]:nodes:[] 3: nodes a1, a2 and b1 (host)

The <field> elements only see the element being iterated: in the second case they cannot obtain the cluster name, and when iterating an object with * they cannot obtain each member’s name. When this is needed, the response is restructured with a transformation. For example, to discover the nodes together with their cluster:

<query name="LocalNodeDiscovery" module="API" type="REST"
       transport="GET" encode="Json" discovery="yes">
    <url>clusters</url>
    <script language="Javascript"><![CDATA[
        function flatten(status_code, result) {
            if (status_code != 200) throw "ZBX_NOTSUPPORTED";
            var res = { nodes: [] };
            for (var i = 0; i < result.clusters.length; i++) {
                var c = result.clusters[i];
                for (var j = 0; j < c.nodes.length; j++)
                    res.nodes.push({ cluster: c.name, host: c.nodes[j].host });
            }
            return res;
        }
        ]]>
    </script>
    <transform language="Javascript" function="flatten"/>
    <item name="local.api.node.discovery" path="nodes:[]"/>
    <field name="CLUSTER" path="cluster"/>
    <field name="HOST"    path="host"/>
</query>
{"data":[ {"{#CLUSTER}":"A","{#HOST}":"a1"}, {"{#CLUSTER}":"A","{#HOST}":"a2"},
          {"{#CLUSTER}":"B","{#HOST}":"b1"} ]}

10.4 SOAP queries#

A SOAP query calls a method of a SOAP service with fixed arguments and extracts the values from the response:

<query name="GoliveWS" module="golive" type="SOAP" transport="POST">
    <url>FoundationEnvironmentManager</url>
    <method uri="http://oracle.e1.bssv.JP000000/" name="getCurrentUserProfile"
            action="">
        <argument name="environment" type="string" value="JPD920"/>
    </method>
    <item name="golive.ws.addressnumber" path="addressNumber" type="INT" default="0"/>
    <item name="golive.ws.language"      path="userProfile:language" type="STRING"/>
    <item name="golive.ws.exectime"      built_in="exec_time"/>
</query>
Element / attribute Description
type="SOAP" <query> attribute. Mandatory.
transport="POST" Mandatory.
<url> Mandatory. Service path, relative to BaseURL.
<method> Method invoked: uri (method namespace), name (method name) and action (value of the SOAPAction HTTP header, may be empty).
<argument> Method argument: name, type (string or int) and value. There can be several. Values are fixed.
<item path type> As in REST, applied to the JSON obtained from the response.
<transform> Function that processes the response JSON (section 10.4.2).

With this definition, Databbix sends to BaseURL/FoundationEnvironmentManager a POST with the header SOAPAction: "" and a body similar to this one:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" ...>
  <SOAP-ENV:Body>
    <ns1:getCurrentUserProfile xmlns:ns1="http://oracle.e1.bssv.JP000000/">
      <environment xsi:type="xsd:string">JPD920</environment>
    </ns1:getCurrentUserProfile>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

If the monitor uses the Oasis authentication engine, a WS-Security SOAP header with the user and password from the .conf is also added.

10.4.1 Converting the SOAP response to JSON#

Databbix converts into JSON the method response element, that is, the first child of <Body>. That element is the root of the JSON: its name is not part of the paths. The conversion follows these rules:

  1. An element with child elements becomes an object. Each child is a member with its local name, without the namespace prefix.

  2. If an element has several children with the same name, that member is an array with all their values, in order.

  3. An element without children becomes a string, even if its content is a number or a boolean, and even if it has an xsi:type attribute.

  4. XML attributes are ignored.

  5. The order of the object’s members may not match the XML order. This does not affect paths.

Since all simple values are strings, transformation functions must convert them to operate on them (Number(r.sessions) in JavaScript, int(r[’sessions’]) in Python). Items do not need this: type="INT" sends the text as is and turns true/false into 1/0.

Example. Service response:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
    <ns2:getCurrentUserProfileResponse xmlns:ns2="http://oracle.e1.bssv.JP000000/">
      <addressNumber>4242</addressNumber>
      <active xsi:type="xsd:boolean">true</active>
      <sessions xsi:type="xsd:int">3</sessions>
      <userProfile>
        <language>E</language>
        <dateFormat>MDY</dateFormat>
      </userProfile>
      <role>ADMIN</role>
      <role>USER</role>
    </ns2:getCurrentUserProfileResponse>
  </soap:Body>
</soap:Envelope>

Resulting JSON (which the <transform> function receives, if there is one):

{
  "addressNumber": "4242",
  "active": "true",
  "sessions": "3",
  "userProfile": { "language": "E", "dateFormat": "MDY" },
  "role": [ "ADMIN", "USER" ]
}

Paths and values obtained

path type Value
addressNumber INT 4242
active INT 1
sessions INT 3
userProfile:language STRING E
role:[0] STRING ADMIN
role:[1] STRING USER

Repeated elements.  An element only becomes an array if it appears more than once. If the user in the example had a single <role>, the JSON would be "role": "ADMIN" and the path role:[0] would fail. When an element may appear once or several times, normalise the response with a transformation (section 10.4.2).

SOAP errors#

If the service answers with a Fault, the converted element is the <Fault> itself:

<soap:Body>
  <soap:Fault>
    <faultcode>soap:Server</faultcode>
    <faultstring>User not authorized</faultstring>
  </soap:Fault>
</soap:Body>
{ "faultcode": "soap:Server", "faultstring": "User not authorized" }

Without a transformation, the item paths do not exist in this JSON and the items become not supported (or take their default value). With a transformation, the error can be detected and handled.

10.4.2 Transforming SOAP responses#

The <transform> function receives (status_code, result), as in REST, but status_code is always 0 and result is the JSON obtained with the rules above. This example detects Faults, normalises role so that it is always an array and computes derived values:

<query name="LocalUserProfile" module="golive" type="SOAP" transport="POST">
    <url>FoundationEnvironmentManager</url>
    <method uri="http://oracle.e1.bssv.JP000000/" name="getCurrentUserProfile" action=""/>
    <script language="Javascript"><![CDATA[
        function profile(status_code, r) {
            if (r.faultcode !== undefined) {
                log_info("SOAP Fault:", r.faultstring);
                throw "ZBX_NOTSUPPORTED";
            }
            var roles = r.role;
            if (roles === undefined)
                roles = [];
            else if (!(roles instanceof Array))
                roles = [ roles ];                 // a single <role>
            return {
                address:  Number(r.addressNumber),
                nroles:   roles.length,
                admin:    roles.indexOf("ADMIN") >= 0,
                language: r.userProfile ? r.userProfile.language : ""
            };
        }
        ]]>
    </script>
    <transform language="Javascript" function="profile"/>
    <item name="local.golive.address"  path="address"  type="INT"/>
    <item name="local.golive.nroles"   path="nroles"   type="INT"/>
    <item name="local.golive.admin"    path="admin"    type="INT"/>
    <item name="local.golive.language" path="language" type="STRING"/>
</query>

Values obtained with the response of the previous example

Key Value
local.golive.address 4242
local.golive.nroles 2
local.golive.admin 1
local.golive.language E

The same function in Python:

def profile(status_code, r):
    if 'faultcode' in r:
        log_info("SOAP Fault:", r.get('faultstring'))
        raise Exception("ZBX_NOTSUPPORTED")
    roles = r.get('role', [])
    if not isinstance(roles, list):
        roles = [roles]
    return {
        'address':  r.get('addressNumber'),
        'nroles':   len(roles),
        'admin':    'ADMIN' in roles,
        'language': r.get('userProfile', {}).get('language', '')
    }

10.5 Runtime data#

The Webservice engine offers this runtime data:

Name Value
DATABBIX.PROGRAM_NAME databbix, or queryexplorer in that utility.
CONN.TYPE Webservice.

Since queries in this engine have no <when> conditions, this data can only be read with getRuntimeValue in queryexplorer.

10.6 Authentication#

10.6.1 Authentication engines and methods#

Two things must be distinguished:

Authentication engine.

Written in C++ inside Databbix, it implements a complete mechanism: how credentials are sent, how a token is obtained and renewed, etc. There are four: HTTPAuth, RESTToken, JDE and Oasis.

Authentication method.

Defined in a query file with <authentication>. It takes an engine (type attribute) and customises it for a particular service with JavaScript or Python functions and other elements (login URL, headers, where the token goes…). It is identified by its name attribute.

Engine Services Direct use Customisation with a method
HTTPAuth REST and SOAP Yes: Credentials = method=BASIC… Optional: <auth-generate>, <header>, <verify-call>.
RESTToken REST No (error RESTToken cannot be directly used) Mandatory: <login>, <token>, <verify-call>.
JDE REST Yes Not supported: a method of this type only gives it another name.
Oasis SOAP Yes Not supported: a method of this type only gives it another name.

10.6.2 How the authentication is chosen#

The Webservice.Authenticator parameter of the .conf contains a name. Databbix looks it up in this order:

  1. An authentication method in the monitor’s query files: an <authentication> whose name matches. If found, it uses the engine given by its type, customised with the method’s functions.

  2. An authentication engine with that name. If found, it is used directly, without customisation, with the credentials from the .conf. (The name with the Authenticator suffix is also accepted, such as HTTPAuthAuthenticator.)

  3. If neither is found, the monitor cannot connect and logs the error.

Note.  Since methods are looked up first, a method whose name matches an engine’s (for example, name="HTTPAuth") hides that engine. Use your own names for methods (AISAuth, ApiKeyAuth…).

10.6.3 Examples#

Engine used directly: there is no method called JDE in the XML files, so the JDE engine is used as is.

[Webservice]
BaseURL = http://jdesm.example.com:8999/manage/mgmtrestservice/
Authenticator = JDE
UserName = jde_admin
Password = secret

Authentication method: the XML file defines AISAuth, which customises the RESTToken engine, and the .conf uses it by name.

<Queries engine="Webservice">
    <authentication type="RESTToken" name="AISAuth">
        <script language="Javascript"> ... </script>
        <login type="JsonPost"> ... </login>
        <token type="Header" name="jde-AIS-Auth"/>
        <verify-call language="Javascript" function="verifyCall"/>
    </authentication>
    <query ...> ... </query>
</Queries>
[Webservice]
Authenticator = AISAuth

Programming the functions of an authentication method is described in section 12.7.