10 Motor Webservice
El motor Webservice obtiene los datos llamando a servicios web por HTTP. Admite dos tipos de servicio, REST y SOAP, que pueden convivir en un mismo fichero de consultas y un mismo monitor.
10.1 REST y SOAP#
Los dos tipos comparten la conexión (URL base, TLS, autenticación) y el modo de extraer los valores: en ambos casos la respuesta se convierte en un documento JSON y cada ítem indica una ruta dentro de él. Se diferencian en cómo se construye la petición y en el formato original de la respuesta:
REST (type="REST") |
SOAP (type="SOAP") |
|
|---|---|---|
| Petición | GET o POST a una URL. |
POST de un sobre XML SOAP con un método y sus argumentos. |
| Parámetros de la clave | Se pueden enviar en la URL (urlparameter) o usar para elegir un elemento de la respuesta (path con <base>). |
No se usan: los argumentos del método son fijos en el XML. |
| Respuesta | JSON. | XML SOAP, que Databbix convierte en JSON (sección 10.4.1). |
status_code en <transform> |
Código HTTP de la respuesta. | Siempre 0; los errores llegan como un Fault en el propio JSON. |
| Descubrimientos | Sí. | No. |
| Autenticación | HTTPAuth, RESTToken, JDE. |
Oasis (WS-Security) o HTTPAuth. |
10.2 Configuración: sección [Webservice]#
| Parámetro | Descripción |
|---|---|
BaseURL |
Obligatorio. URL base. Las URL de las consultas son relativas a ella. |
UserName, Password |
Credenciales. |
Credentials |
Credencial adicional con la forma clave=valor (entorno, rol, método…), según el motor de autenticación. Se puede repetir. |
Authenticator |
Nombre de un método de autenticación definido en los XML o, si no existe, de un motor de autenticación: HTTPAuth, JDE u Oasis (sección 10.6). |
Timeout |
Tiempo máximo de espera en milisegundos (defecto: 20000). |
AuthPersistFile |
Fichero donde se guarda el estado de la autenticación (por ejemplo, el token) entre reinicios. |
VerifyCertificate |
Verificar el certificado del servidor (defecto: yes). |
CertCAFile |
Fichero de CA que se usa en lugar del almacén del sistema. |
ClientCertificate |
Certificado de cliente (PEM) para autenticación TLS. |
ClientPrivKey |
Clave privada del certificado de cliente. Obligatoria si se usa ClientCertificate. |
ClientPrivKeyPass |
Contraseña de la clave privada, si está cifrada. |
DisableContent Encoding |
Desactiva Content-Encoding, para servidores antiguos (defecto: no). |
[General]
Name = JDEPROD
Engine = Webservice
QueryFile = /etc/databbix/query-jde.xml
[Zabbix]
Server = zabbix.empresa.com
Port = 10051
[Data]
Module = JDE
[Webservice]
BaseURL = http://10.1.1.45:9888/restservice
UserName = monitor
Password = secreto
Authenticator = JDE10.3 Consultas REST#
<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>| Elemento / atributo | Descripción |
|---|---|
type="REST" |
Atributo de <query>. Obligatorio. |
transport |
Obligatorio. GET o POST. |
encode |
Formato de la respuesta. Solo se admite Json. |
<url> |
Obligatorio. Ruta relativa a BaseURL. |
<parameter urlparameter> |
Envía el parámetro de la clave en la URL, como ?nombre=valor (sección 10.3.2). |
<base path> |
Array de la respuesta que se recorre para buscar el elemento que indica la clave (sección 10.3.3). |
<parameter path> |
Con <base>: campo de cada elemento que se compara con el parámetro de la clave. |
<item path type> |
Ruta del valor dentro del JSON (sección 10.3.1) y tipo: INT o STRING. Ambos obligatorios, salvo en ítems built_in. |
<transform> |
Función que procesa la respuesta antes de extraer los valores (sección 12.6). |
Sin <transform>, una respuesta con código HTTP distinto de 200 es un error y todos los ítems de la consulta pasan a no soportados.
10.3.1 Rutas JSON#
Una ruta indica cómo llegar a un valor desde la raíz del documento JSON. Se compone de pasos separados por dos puntos (:):
| Paso | Significado |
|---|---|
nombre |
Miembro nombre de un objeto. |
[n] |
Elemento n de un array, empezando en 0. |
[] |
Todos los elementos de un array. Solo en <base> y en el <item> de un descubrimiento. |
* |
Todos los miembros de un objeto. Solo en <base> y en el <item> de un descubrimiento. |
La ruta de un ítem tiene que terminar en un valor simple (texto, número, booleano o null). Dado este documento:
{
"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
}
Rutas y valores obtenidos
| path | type | Valor |
|---|---|---|
| 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 | (cadena vacía) |
| server:host | STRING | no soportado: el miembro no existe |
| jobs:[5]:status | STRING | no soportado: el array tiene 2 elementos |
| server | STRING | error: no es un valor simple |
En los casos no soportado, si el ítem tiene atributo default, se envía ese valor. Con type="INT", los booleanos se convierten en 1 y 0.
Nota. Los nombres de miembro no pueden contener dos puntos, porque es el separador de pasos. Si el servicio los usa, reestructure la respuesta con una transformación (sección 12.6).
10.3.2 Parámetros en la URL#
Cada <parameter> con urlparameter añade a la URL un parámetro con ese nombre y el valor de la clave. La consulta se ejecuta una vez por cada clave distinta:
<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>Llamadas y valores obtenidos
| Clave | URL llamada | Valor |
|---|---|---|
| ts.jde.instance.state[ENT1] | …/instancestate?instanceName=ENT1 | RUNNING |
| ts.jde.instance.state[WEB1] | …/instancestate?instanceName=WEB1 | STOPPED |
En cada llamada el servicio responde, por ejemplo, {"instanceState":"RUNNING"}, y la ruta instanceState extrae el valor.
10.3.3 Filtro sobre la respuesta#
Cuando el servicio devuelve de una vez la lista de todos los objetos, es más eficiente hacer una sola llamada y elegir el elemento en la respuesta, igual que una consulta FILTER de base de datos. Para ello:
<base path="…">indica el array que se recorre (su ruta termina en[]);cada
<parameter path="…">indica un campo de cada elemento que se compara con un parámetro de la clave;las rutas de los ítems son relativas al elemento elegido.
<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>Con el documento de la sección anterior como respuesta:
Valores obtenidos
| Clave | Elemento elegido | Valor |
|---|---|---|
| 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] | ninguno | UNKNOWN (default) |
| local.api.job.duration[reindex] | ninguno | no soportado |
Las cinco claves se resuelven con una sola llamada al servicio. Con type="INT" en el <parameter>, la comparación es numérica (por ejemplo, parameter path="id" y la clave …[2]).
10.3.4 Descubrimientos REST#
La ruta del <item> de un descubrimiento recorre un array ([]) o los miembros de un objeto (*). Cada elemento recorrido es una entidad descubierta, y las rutas de los <field> son relativas a él:
<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>Con el documento de la sección 10.3.1, el valor del ítem es:
{"data":[ {"{#JOB}":"backup", "{#JOBID}":"1"},
{"{#JOB}":"purge", "{#JOBID}":"2"} ]}
Recorridos anidados#
Una ruta puede tener varios pasos de recorrido. Con esta respuesta:
{ "clusters": [
{ "name": "A", "nodes": [ {"host": "a1"}, {"host": "a2"} ] },
{ "name": "B", "nodes": [ {"host": "b1"} ] }
] }
Ruta del ítem y entidades descubiertas
| path del item | Entidades (y field path="…" disponibles) |
|---|---|
| clusters:[] | 2: los clusters A y B (name, nodes:[0]:host…) |
| clusters:[]:nodes:[] | 3: los nodos a1, a2 y b1 (host) |
Los <field> solo ven el elemento recorrido: en el segundo caso no pueden obtener el nombre del cluster, y al recorrer un objeto con * no pueden obtener el nombre de cada miembro. Cuando se necesita, se reestructura la respuesta con una transformación. Por ejemplo, para descubrir los nodos junto con su cluster:
<query name="LocalNodeDiscovery" module="API" type="REST"
transport="GET" encode="Json" discovery="yes">
<url>clusters</url>
<script language="Javascript"><![CDATA[
function aplanar(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="aplanar"/>
<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 Consultas SOAP#
Una consulta SOAP llama a un método de un servicio SOAP con argumentos fijos y extrae los valores de la respuesta:
<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>| Elemento / atributo | Descripción |
|---|---|
type="SOAP" |
Atributo de <query>. Obligatorio. |
transport="POST" |
Obligatorio. |
<url> |
Obligatorio. Ruta del servicio, relativa a BaseURL. |
<method> |
Método que se invoca: uri (espacio de nombres del método), name (nombre del método) y action (valor de la cabecera HTTP SOAPAction, puede estar vacío). |
<argument> |
Argumento del método: name, type (string o int) y value. Puede haber varios. Los valores son fijos. |
<item path type> |
Igual que en REST, sobre el JSON obtenido de la respuesta. |
<transform> |
Función que procesa el JSON de la respuesta (sección 10.4.2). |
Con esta definición, Databbix envía a BaseURL/FoundationEnvironmentManager un POST con la cabecera SOAPAction: "" y un cuerpo similar a este:
<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>Si el monitor usa el motor de autenticación Oasis, se añade además una cabecera SOAP WS-Security con el usuario y la contraseña del .conf.
10.4.1 Conversión de la respuesta SOAP a JSON#
Databbix convierte en JSON el elemento de respuesta del método, es decir, el primer hijo de <Body>. Ese elemento es la raíz del JSON: su nombre no forma parte de las rutas. La conversión sigue estas reglas:
Un elemento con elementos hijos se convierte en un objeto. Cada hijo es un miembro con su nombre local, sin el prefijo del espacio de nombres.
Si un elemento tiene varios hijos con el mismo nombre, ese miembro es un array con todos sus valores, en orden.
Un elemento sin hijos se convierte en un texto, aunque su contenido sea un número o un booleano, y aunque lleve un atributo
xsi:type.Los atributos XML se ignoran.
El orden de los miembros del objeto puede no coincidir con el del XML. Esto no afecta a las rutas.
Como todos los valores simples son texto, en las funciones de transformación hay que convertirlos para operar con ellos (Number(r.sessions) en JavaScript, int(r[’sessions’]) en Python). En los ítems no hace falta: type="INT" envía el texto tal cual y convierte true/false en 1/0.
Ejemplo. Respuesta del servicio:
<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>S</language>
<dateFormat>DMY</dateFormat>
</userProfile>
<role>ADMIN</role>
<role>USER</role>
</ns2:getCurrentUserProfileResponse>
</soap:Body>
</soap:Envelope>JSON que se obtiene (y que recibe la función de <transform>, si la hay):
{
"addressNumber": "4242",
"active": "true",
"sessions": "3",
"userProfile": { "language": "S", "dateFormat": "DMY" },
"role": [ "ADMIN", "USER" ]
}
Rutas y valores obtenidos
| path | type | Valor |
|---|---|---|
| addressNumber | INT | 4242 |
| active | INT | 1 |
| sessions | INT | 3 |
| userProfile:language | STRING | S |
| role:[0] | STRING | ADMIN |
| role:[1] | STRING | USER |
Elementos repetidos. Un elemento solo se convierte en array si aparece más de una vez. Si el usuario del ejemplo tuviera un único <role>, el JSON sería "role": "ADMIN" y la ruta role:[0] fallaría. Cuando un elemento puede aparecer una o varias veces, normalice la respuesta con una transformación (sección 10.4.2).
Errores SOAP#
Si el servicio responde con un Fault, el elemento convertido es el propio <Fault>:
<soap:Body>
<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>User not authorized</faultstring>
</soap:Fault>
</soap:Body>{ "faultcode": "soap:Server", "faultstring": "User not authorized" }
Sin transformación, las rutas de los ítems no existen en este JSON y los ítems pasan a no soportados (o toman su valor default). Con una transformación se puede detectar el error y decidir qué hacer.
10.4.2 Transformación de respuestas SOAP#
La función de <transform> recibe (status_code, result), igual que en REST, pero status_code vale siempre 0 y result es el JSON obtenido con las reglas anteriores. Este ejemplo detecta los Fault, normaliza role para que sea siempre un array y calcula valores derivados:
<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 perfil(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 ]; // un solo <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="perfil"/>
<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>Valores obtenidos con la respuesta del ejemplo anterior
| Clave | Valor |
|---|---|
| local.golive.address | 4242 |
| local.golive.nroles | 2 |
| local.golive.admin | 1 |
| local.golive.language | S |
La misma función en Python:
def perfil(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 Datos de ejecución#
El motor Webservice ofrece estos datos de ejecución:
| Nombre | Valor |
|---|---|
DATABBIX.PROGRAM_NAME |
databbix, o queryexplorer en esa utilidad. |
CONN.TYPE |
Webservice. |
Como las consultas de este motor no tienen condiciones <when>, estos datos solo se consultan con getRuntimeValue en queryexplorer.
10.6 Autenticación#
10.6.1 Motores y métodos de autenticación#
Hay que distinguir dos cosas:
- Motor de autenticación.
-
Está programado en C++ dentro de Databbix e implementa un mecanismo completo: cómo se envían las credenciales, cómo se obtiene y renueva un token, etc. Hay cuatro:
HTTPAuth,RESTToken,JDEyOasis. - Método de autenticación.
-
Se define en un fichero de consultas con
<authentication>. Toma un motor (atributotype) y lo personaliza para un servicio concreto con funciones en JavaScript o Python y otros elementos (URL de login, cabeceras, dónde va el token…). Se identifica por su atributoname.
| Motor | Servicios | Uso directo | Personalización con un método |
|---|---|---|---|
HTTPAuth |
REST y SOAP | Sí: Credentials = method=BASIC… |
Opcional: <auth-generate>, <header>, <verify-call>. |
RESTToken |
REST | No (error RESTToken cannot be directly used) | Obligatoria: <login>, <token>, <verify-call>. |
JDE |
REST | Sí | No admite: un método de este tipo solo le da otro nombre. |
Oasis |
SOAP | Sí | No admite: un método de este tipo solo le da otro nombre. |
10.6.2 Cómo se elige la autenticación#
El parámetro Webservice.Authenticator del .conf contiene un nombre. Databbix lo busca en este orden:
Un método de autenticación en los ficheros de consultas del monitor: un
<authentication>cuyonamecoincida. Si lo encuentra, usa el motor que indica sutype, personalizado con las funciones del método.Un motor de autenticación con ese nombre. Si lo encuentra, lo usa directamente, sin personalizar, con las credenciales del
.conf. (Se admite también el nombre con el sufijoAuthenticator, comoHTTPAuthAuthenticator.)Si no encuentra ninguno de los dos, el monitor no puede conectar y lo registra en su log.
Nota. Como los métodos se buscan primero, un método cuyo name coincida con el de un motor (por ejemplo, name="HTTPAuth") oculta ese motor. Use nombres propios para los métodos (AISAuth, ApiKeyAuth…).
10.6.3 Ejemplos#
Motor usado directamente: no hay ningún método llamado JDE en los XML, así que se usa el motor JDE tal cual.
[Webservice]
BaseURL = http://jdesm.empresa.com:8999/manage/mgmtrestservice/
Authenticator = JDE
UserName = jde_admin
Password = secretoMétodo de autenticación: el XML define AISAuth, que personaliza el motor RESTToken, y el .conf lo usa por su nombre.
<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 = AISAuthLa programación de las funciones de autenticación se describe en la sección 12.7.