9 Motor Database

El motor Database obtiene los datos ejecutando sentencias SQL sobre una base de datos. Es el motor por defecto: un monitor sin General.Engine usa este motor.

9.1 Configuración: sección [Database]#

Parámetro Descripción
DBDriver Obligatorio. Driver: Oracle, PGsql u ODBC.
ConnectionString Obligatorio. Cadena de conexión. Su formato depende del driver (sección 9.2).
UserName Obligatorio. Usuario de la base de datos.
Password Obligatorio. Contraseña.
ConnectOptions Opciones especiales. La única admitida es SYSDBA (necesaria para ASM).

El usuario de base de datos solo necesita permisos de lectura sobre las vistas que consultan los XML.

9.2 Drivers#

El parámetro DBDriver elige el driver con el que se conecta el monitor. Cada driver interpreta a su manera la cadena de conexión, admite unos contextos concretos y ofrece unos datos de ejecución propios.

DBDriver Bases de datos Contextos Cadena de conexión
Oracle Oracle (OCI) Container Alias de tnsnames.ora, Easy Connect o descriptor completo.
PGsql PostgreSQL (libpq) Database Pares clave=valor de libpq.
ODBC Cualquiera con driver ODBC (SQL Server, DB2/400…) Database Cadena de SQLDriverConnect: DSN=nombre o DRIVER={…};…

Nota.  PostgreSQL y Oracle también se pueden usar a través del driver ODBC, con un driver ODBC del fabricante o de terceros. Aun así, se recomienda usar el driver nativo (PGsql u Oracle) siempre que sea posible: es el que conoce los contextos de cada base de datos (Database en PostgreSQL, Container en Oracle) y el que ofrece sus datos de ejecución específicos, como ORACLE.CONTAINER.

En todos los drivers, las variables de enlace de las consultas BIND se escriben siempre con la forma :nombre. El driver las convierte a la sintaxis de cada base de datos ($1, $2… en PostgreSQL, ? en ODBC).

9.2.1 Driver Oracle#

Cadena de conexión.

Se pasa tal cual al cliente de Oracle, así que admite cualquier forma que este acepte:

ConnectionString = oraprod                          # alias de tnsnames.ora
ConnectionString = dbhost.empresa.com:1521/ORAPROD  # Easy Connect
ConnectionString = (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=dbhost)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=ORAPROD)))

Para usar alias, la variable TNS_ADMIN del servicio debe apuntar al directorio del tnsnames.ora.

ConnectOptions.

SYSDBA conecta con privilegios de administrador (necesario para ASM). Sin valor, conexión normal.

Contextos.

Container: el contenedor (PDB) activo, que se cambia con ALTER SESSION SET CONTAINER. Solo está disponible en Oracle 12c o posterior, en una base de datos multitenant y conectando al contenedor raíz (CDB$ROOT). En cualquier otro caso, el driver no admite contextos y ORACLE.CONTAINER vale 0.

Variables de enlace.

Nativas de Oracle (:nombre).

9.2.2 Driver PGsql#

Cadena de conexión.

Pares clave=valor de libpq separados por espacios (host, port, dbname, sslmode, connect_timeout…). El usuario y la contraseña se añaden desde UserName y Password; no los ponga en la cadena.

ConnectionString = host=pg01.empresa.com port=5432 dbname=postgres sslmode=require
ConnectOptions.

No se usa.

Contextos.

Database: la base de datos activa. Como PostgreSQL no permite cambiar de base de datos en una conexión, el driver abre una conexión por cada base de datos que se usa (sustituyendo el dbname de la cadena) y las mantiene abiertas.

Variables de enlace.

Se convierten a $1, $2…

Atención.  Incluya siempre dbname en la cadena de conexión: el driver lo usa como base de datos por defecto y lo sustituye al cambiar de contexto. La contraseña se añade a la cadena sin comillas, así que no puede contener espacios.

9.2.3 Driver ODBC#

Cadena de conexión.

Se pasa a SQLDriverConnect añadiendo ;UID=…;PWD=… con el usuario y la contraseña. Puede referirse a una fuente de datos de odbc.ini o describir la conexión completa:

ConnectionString = DSN=MSSQL_PROD
ConnectionString = DRIVER={FreeTDS};SERVER=mssql.empresa.com;PORT=1433;DATABASE=master

Para usar DSN, la variable ODBCINI del servicio debe apuntar al fichero odbc.ini.

ConnectOptions.

No se usa.

Contextos.

Database: la base de datos (catálogo) activa, que se cambia en la misma conexión con el atributo ODBC SQL_ATTR_CURRENT_CATALOG. Funciona con los drivers que lo admiten, como los de SQL Server.

Variables de enlace.

Se convierten a ?.

Nota.  La contraseña se añade a la cadena sin comillas, así que no puede contener el carácter ;.

9.2.4 Datos de ejecución#

Los datos de ejecución se consultan en las condiciones de <when> con runtime(’NOMBRE’) (sección 9.6) y en queryexplorer con getRuntimeValue.

Nombre Driver Valor
DATABBIX.PROGRAM_NAME todos databbix, o queryexplorer en esa utilidad.
CONN.TYPE todos Database.
DB.DRIVER Oracle Oracle.
PGsql PostgreSQL.
ODBC ODBC.
DB.DATABASE Oracle Oracle.
PGsql PostgreSQL.
ODBC Nombre del producto que informa el driver ODBC (SQL_DBMS_NAME): por ejemplo, Microsoft SQL Server o DB2/400 SQL.
DB.VERSION Oracle Versión del servidor, con la forma mayor.menor.actualización.
PGsql Parámetro server_version del servidor, por ejemplo 15.4.
ODBC Versión que informa el driver ODBC (SQL_DBMS_VER).
ORACLE.CONTAINER Oracle 1 si el contexto Container está disponible; 0 si no.

Así, un mismo XML puede elegir el SQL según el producto (runtime(’DB.DATABASE’) = ’Microsoft SQL Server’), según la versión (major(runtime(’DB.VERSION’)) >= 12) o según si la base de datos es multitenant (runtime(’ORACLE.CONTAINER’) = ’1’).

9.3 Consultas SQL: elementos propios del motor#

Además de los elementos comunes (capítulo 7), las consultas del motor Database usan:

Elemento / atributo Descripción
<sql> Texto de la sentencia. Obligatorio salvo en las consultas constantes. Admite variables ${…} y alternativas con <multiple> (sección 9.6).
<sql context="C"> Solo en descubrimientos: sentencia que devuelve los valores del contexto C que hay que recorrer (sección 9.4.4).
select Atributo de <query>: FILTER o BIND. Sin él, la consulta es básica.
<item column="…"> Columna del resultado de la que sale el valor. No distingue mayúsculas de minúsculas.
<parameter column> En consultas FILTER: columna que se compara con el parámetro.
<parameter bind> En consultas BIND: variable de enlace del SQL.
<parameter context> Contexto en el que se ejecuta la consulta.
<default> Contexto fijo de la consulta (sección 9.5).
<field column|context> En descubrimientos: de dónde sale el valor de la macro.
<context> En <Queries>: declaración de un contexto.

Nota.  Conviene dar siempre un alias explícito (as nombre) a las columnas calculadas, y usar esos alias en column.

9.4 Tipos de consulta#

Los ejemplos de esta sección muestran, para cada tipo, el resultado de la sentencia SQL en un momento dado y el valor que obtiene cada clave de Zabbix a partir de él.

9.4.1 Consulta básica#

La sentencia devuelve exactamente una fila. Cada <item> toma el valor de una columna.

<query name="PGWriter" module="PG">
    <sql><![CDATA[
        select checkpoints_timed, checkpoints_req from pg_stat_bgwriter
        ]]>
    </sql>
    <item name="tez.pg.checkpointtimed" column="checkpoints_timed"/>
    <item name="tez.pg.checkpointreq"   column="checkpoints_req"/>
</query>

Resultado de la sentencia

checkpoints_timed checkpoints_req
1842 37

Valores obtenidos

Clave Valor
tez.pg.checkpointtimed 1842
tez.pg.checkpointreq 37

Si la sentencia devuelve cero filas o más de una, todos los ítems de la consulta pasan a no soportados.

9.4.2 Consulta de filtro (select="FILTER")#

La sentencia devuelve muchas filas. Cada <parameter> con column relaciona un parámetro de la clave con una columna, y cada ítem se queda con la fila cuyas columnas coinciden con los parámetros de su clave. La sentencia se ejecuta una sola vez para todos los ítems, así que es la forma más eficiente de vigilar colecciones de objetos (tablespaces, bases de datos, trabajos…).

<query name="PGSessions" module="PG" select="FILTER">
    <sql><![CDATA[
        select datname,
               count(st.pid) as n,
               sum(case st.state when 'active' then 1 else 0 end) as active
        from pg_database db
             left outer join pg_stat_activity st using (datname)
        where datname not like 'template%'
        group by datname
        ]]>
    </sql>
    <parameter column="datname" order="1" type="STRING"/>
    <item name="tez.pg.sessions"       column="n"/>
    <item name="tez.pg.sessionsactive" column="active" default="0"/>
</query>

Resultado de la sentencia

datname n active
postgres 3 1
ventas 12 4
rrhh 0 0

Valores obtenidos

Clave Fila elegida Valor
tez.pg.sessions[ventas] ventas 12
tez.pg.sessionsactive[ventas] ventas 4
tez.pg.sessions[rrhh] rrhh 0
tez.pg.sessions[compras] ninguna no soportado
tez.pg.sessionsactive[compras] ninguna 0 (atributo default)
tez.pg.sessions[Ventas] ninguna no soportado

Las cuatro primeras claves se resuelven con una única ejecución de la sentencia. La comparación es exacta: distingue mayúsculas y minúsculas (Ventas no coincide con ventas) y solo elimina los espacios de los extremos del parámetro. Si una columna tiene espacios de relleno (tipo char), recórtelos en el SQL con trim().

Varias columnas de filtro#

Con varios parámetros de filtro, la clave lleva un parámetro por cada uno, en el orden que indica order, y la fila tiene que coincidir en todos:

<query name="MSStateRepl" module="MSSQL" select="FILTER">
    <sql><![CDATA[ exec sp_replmonitorhelppublication ]]></sql>
    <default context="Database" value="distribution"/>
    <parameter column="publisher_db" order="1" type="STRING"/>
    <parameter column="publication"  order="2" type="STRING"/>
    <item name="tez.mssql.repl.status" column="status"/>
</query>

Resultado de la sentencia

publisher_db publication status
ventas pub_pedidos 3
ventas pub_clientes 2
compras pub_pedidos 6

Valores obtenidos

Clave Valor
tez.mssql.repl.status[ventas,pub_pedidos] 3
tez.mssql.repl.status[compras,pub_pedidos] 6
tez.mssql.repl.status[ventas] no soportado (falta un parámetro)

Parámetros numéricos#

Con type="INT", el parámetro y la columna se comparan como números. Así, la clave no tiene que reproducir el formato exacto de la columna:

<parameter column="job_id" order="1" type="INT"/>
<item name="local.job.status" column="status"/>

Resultado de la sentencia y valores obtenidos

job_id status Clave Valor
7 OK local.job.status[7] OK
42 ERROR local.job.status[042] ERROR

Con type="STRING", la clave local.job.status[042] no encontraría la fila, porque el texto 042 es distinto de 42.

9.4.3 Consulta con variables de enlace (select="BIND")#

El parámetro de la clave se pasa a la sentencia como variable de enlace. La sentencia se ejecuta una vez por cada clave distinta y debe devolver exactamente una fila.

<query name="PGParameter" module="PG" select="BIND">
    <sql><![CDATA[
        select setting from pg_settings where name = :name
        ]]>
    </sql>
    <parameter bind=":name" order="1" type="STRING"/>
    <item name="tez.pg.param" column="setting"/>
</query>

Ejecuciones y valores obtenidos

Clave Valor de :name Valor
tez.pg.param[max_connections] ’max_connections’ 200
tez.pg.param[work_mem] ’work_mem’ 4096
tez.pg.param[no_existe] ’no_existe’ no soportado (0 filas)

La variable se escribe siempre como :nombre, sea cual sea el driver (sección 9.2). Con type="INT", el valor se enlaza como número.

Atención.  Como se ejecuta una vez por clave, una consulta BIND es mucho más costosa que una FILTER cuando hay muchas claves. Úsela solo cuando el conjunto de valores posibles sea muy grande y se consulten pocos.

9.4.4 Consulta de descubrimiento#

Cada fila del resultado es una entidad descubierta, y cada <field> toma el valor de una columna (column) o de un contexto (context).

<query name="TSDiscover" module="DB" discovery="yes">
    <sql><![CDATA[
        select tablespace_name, contents
        from dba_tablespaces where status = 'ONLINE'
        ]]>
    </sql>
    <item name="tez.ora.tspace.discovery"/>
    <field name="TSNAME"   column="tablespace_name"/>
    <field name="CONTENTS" column="contents"/>
</query>

Resultado de la sentencia

tablespace_name contents
SYSTEM PERMANENT
USERS PERMANENT
TEMP TEMPORARY

Valor del ítem tez.ora.tspace.discovery:

{"data":[
  {"{#TSNAME}":"SYSTEM", "{#CONTENTS}":"PERMANENT"},
  {"{#TSNAME}":"USERS",  "{#CONTENTS}":"PERMANENT"},
  {"{#TSNAME}":"TEMP",   "{#CONTENTS}":"TEMPORARY"}
]}

En Zabbix, el prototipo tez.ora.tspace.pctfree[{#TSNAME}] genera los ítems tez.ora.tspace.pctfree[SYSTEM], [USERS] y [TEMP], que resuelve una consulta FILTER con parameter column="tablespace_name".

Los descubrimientos solo admiten parámetros de contexto.

Descubrimiento a través de contextos#

Un descubrimiento puede recorrer todos los valores de un contexto: por ejemplo, listar los filegroups de todas las bases de datos de un SQL Server. Para ello se añade un <sql> con atributo context que devuelve, en su primera columna, los valores que hay que recorrer. La sentencia principal se ejecuta una vez en cada uno:

<query name="MSDiscoverFG" module="MSSQL" discovery="yes">
    <sql context="Database"><![CDATA[
        select name from sys.databases where name not in ('model')
        ]]>
    </sql>
    <sql><![CDATA[
        select name from sys.filegroups
        ]]>
    </sql>
    <item name="tez.mssql.fg.discovery"/>
    <field name="DBNAME" context="Database"/>
    <field name="FGNAME" column="name"/>
</query>

Resultado de las sentencias

Sentencia de contexto Sentencia principal en cada base de datos
name base de datos name
master master PRIMARY
ventas ventas PRIMARY
ventas HISTORICO

Valor del ítem tez.mssql.fg.discovery:

{"data":[
  {"{#DBNAME}":"master", "{#FGNAME}":"PRIMARY"},
  {"{#DBNAME}":"ventas", "{#FGNAME}":"PRIMARY"},
  {"{#DBNAME}":"ventas", "{#FGNAME}":"HISTORICO"}
]}

Los valores de contexto en los que no se puede entrar (por ejemplo, una base de datos fuera de línea) se saltan y quedan registrados en el log.

9.5 Contextos#

Un contexto es un estado de la conexión que se puede cambiar entre consultas, como la base de datos activa en SQL Server o PostgreSQL o el contenedor (PDB) en Oracle multitenant. Así, un único monitor con una sola conexión puede consultar varias bases de datos de un mismo servidor. Los contextos solo existen en el motor Database.

9.5.1 Declaración#

Los contextos se declaran en el nivel de <Queries>:

<environment name="PGAdminDB" default="postgres"/>
<context name="Database" module="PG" default="${PGAdminDB}"/>
Atributo Descripción
name Obligatorio. Nombre del contexto. El driver debe saber cambiarlo; los nombres habituales son Database (SQL Server, PostgreSQL) y Container (Oracle multitenant).
module Obligatorio. Módulo al que pertenece.
default Obligatorio. Valor por defecto. Admite variables ${…}.

9.5.2 Uso en consultas#

Hay tres formas de elegir el contexto de una consulta:

  1. Por defecto. Si la consulta no dice nada, se ejecuta con el valor default del contexto.

  2. Fijo para la consulta, con <default>:

    <default context="Database" value="msdb"/>

    Si value vale __IGNORE__ (por ejemplo, a través de una variable de entorno sin configurar), el elemento se ignora y se mantiene el valor por defecto.

  3. Tomado de la clave del ítem, con un parámetro de contexto:

    <parameter context="Database" order="1"/>

9.5.3 Ejemplo: filtro dentro de cada base de datos#

Con un parámetro de contexto, el primer parámetro de la clave elige la base de datos y los siguientes, la fila:

<context name="Database" module="MSSQL" default="master"/>

<query name="MSFGSpace" module="MSSQL" select="FILTER">
    <sql><![CDATA[
        select g.name as filegroup_name,
               sum(cast(fileproperty(f.name, 'SPACEUSED') as bigint) * 8192) as used
        from sys.database_files f
             inner join sys.filegroups g on g.data_space_id = f.data_space_id
        group by g.name
        ]]>
    </sql>
    <parameter context="Database" order="1"/>
    <parameter column="filegroup_name" order="2" type="STRING"/>
    <item name="tez.mssql.fgspace.used" column="used"/>
</query>

Resultado de la sentencia en cada base de datos

base de datos filegroup_name used
master PRIMARY 5242880
ventas PRIMARY 73400320
ventas HISTORICO 524288000

Valores obtenidos

Clave Contexto Valor
tez.mssql.fgspace.used[ventas,HISTORICO] ventas 524288000
tez.mssql.fgspace.used[ventas,PRIMARY] ventas 73400320
tez.mssql.fgspace.used[master,PRIMARY] master 5242880

Databbix agrupa las ejecuciones por contexto: entra en ventas una sola vez, ejecuta la sentencia y resuelve las dos primeras claves con el mismo resultado. Si no puede entrar en un contexto, marca como no soportados los ítems de ese contexto. Si el contexto que falla es el por defecto y ConnLostOnDefaultCtxError = yes, da la conexión por perdida y reconecta.

9.6 SQL alternativo según la base de datos#

Un mismo XML puede servir para distintos productos o versiones de base de datos si el texto SQL se elige al ejecutar, con <multiple>:

<sql>
  <multiple>
    <when condition="runtime('DB.DATABASE') = 'Oracle'"><![CDATA[
        select count(*) as n from ${SvmSchema}.f986110
        where trim(jcfndfuf2) = :jobname
        ]]>
    </when>
    <when condition="runtime('DB.DATABASE') = 'Microsoft SQL Server'"><![CDATA[
        select count(*) as n from ${SvmSchema}.f986110
        where rtrim(jcfndfuf2) = :jobname
        ]]>
    </when>
    <when><![CDATA[
        -- sin condición: se usa si ninguna anterior se cumple
        select count(*) as n from ${SvmSchema}.f986110
        where jcfndfuf2 = :jobname
        ]]>
    </when>
  </multiple>
</sql>

Se usa el primer <when> cuya condición se cumple. Un <when> sin condición se cumple siempre, así que debe ir el último. Si no se cumple ninguna condición, la consulta falla.

Las condiciones son expresiones que pueden usar estas funciones:

Función Descripción
runtime(’X’) Dato de ejecución de la conexión activa, por ejemplo DB.DATABASE o DB.VERSION (sección 9.2.4).
environment(’X’) Valor de una variable de entorno.
major(’X.Y.Z’) Primer número de una versión, como número.
toscalar(’X’) Convierte un texto en número.

Admiten operadores de comparación (=, !=, <, >…) y lógicos (and, or, not). Los datos de ejecución disponibles con cada driver se pueden consultar con el comando getRuntimeNames de queryexplorer.

9.7 Ejemplos de configuración#

9.7.1 Ejemplo: Oracle#

[General]
Name = ORAPROD

[Zabbix]
Server = zabbix.empresa.com
Port = 10051
PassivePort = 10153

[Data]
Module = DB
Module = Instance

[Database]
DBDriver = Oracle
ConnectionString = oraprod          # alias de tnsnames.ora
UserName = zabbix
Password = secreto

9.7.2 Ejemplo: Oracle multitenant#

[General]
Name = ORACDB
QueryFile = /etc/databbix/query-mt.xml

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

[Data]
Module = MT

[Database]
DBDriver = Oracle
ConnectionString = oracdb
UserName = c\043\043zabbix          # c##zabbix
Password = secreto

9.7.3 Ejemplo: ASM#

[General]
Name = asm

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

[Data]
Module = ASM

[Database]
DBDriver = Oracle
ConnectionString = asm
ConnectOptions = SYSDBA
UserName = sys
Password = secreto

9.7.4 Ejemplo: PostgreSQL#

[General]
Name = PGPROD
QueryFile = /etc/databbix/query-pg.xml
QueryFile = /etc/databbix/query-pg-10.xml
Environment = PGAdminDB=postgres

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

[Data]
Module = PG

[Database]
DBDriver = PGsql
ConnectionString = host=pg01.empresa.com port=5432 dbname=postgres
UserName = zabbix
Password = secreto

9.7.5 Ejemplo: SQL Server por ODBC#

Primero se define la fuente de datos en /etc/databbix/odbc.ini:

[MSSQL_PROD]
Driver=FreeTDS
Description=SQL Server de producción
Server=mssql.empresa.com
Port=1433
Database=master

Y después el monitor la referencia por su nombre:

[General]
Name = MSSQLPROD
QueryFile = /etc/databbix/query-mssql.xml

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

[Data]
Module = MSSQL

[Database]
DBDriver = ODBC
ConnectionString = DSN=MSSQL_PROD
UserName = zabbix
Password = secreto