Pandora Plugin Executor Ejecutor genérico de plugins para Pandora FMS. Introducción pandora-plugin_exec es un único binario en Go que sustituye a los plugins por tecnología. Una plantilla YAML describe el trabajo a realizar: Tareas a ejecutar — peticiones HTTP o ejecuciones de comandos locales. Filtros (jq o regexp) que extraen valores de la salida de una tarea hacia variables . Agentes y módulos construidos a partir de esas variables (incluyendo expresiones aritméticas). Un método de transferencia que entrega los datos resultantes a Pandora FMS. El ejecutor ejecuta la plantilla una vez y termina (one-shot). Añadir soporte para una nueva tecnología significa escribir una nueva plantilla YAML — no un plugin nuevo, ni un lenguaje nuevo, ni un pipeline de empaquetado nuevo. Está pensado para usarse junto con el agente de Pandora FMS. template.yml ─▶ ejecutar tareas ─▶ filtrar salidas en variables ─▶ renderizar módulos ─▶ entregar Versión : 0.1.0 ( pandora-plugin_exec --version ). Audiencia: personas que escriben plantillas y agentes de IA que las generan. Todo lo necesario para escribir una plantilla válida está en este archivo. De serie, el plugin incluye plantillas ya construidas bajo templates/ : ejemplos de aprendizaje y sustitutos directos de plugins de agente que antes venían con el agente. Consulta Plantillas incluidas . Matriz de compatibilidad Por ahora solo se ha probado Linux. Windows está planificado, pero todavía no se ha probado. Plataforma / runtime Estado Linux amd64 Probado Linux arm64 No establecido Windows amd64 No establecido macOS No establecido El binario entregado es estático, por lo que no tiene dependencias de runtime en el host. Prerrequisitos Para ejecutar el binario: No se requiere runtime de Go, ni jq externo, ni Perl/Python — el binario es estático e incorpora el motor jq ( gojq ). Se usa /bin/sh para ejecutar los comandos de las tareas local , así que los comandos referenciados por la plantilla ( df , ss , awk , archivos de /proc , etc.) deben existir en el host. Por modo de transferencia: Modo Requisito agent_plugin Un agente de software de Pandora FMS que pueda invocar el binario mediante module_plugin tentacle El binario tentacle_client (en $PATH , o su ruta configurada mediante transfer.tentacle.binary ) y acceso de red al servidor de Pandora FMS (puerto por defecto 41121 ) local Permiso de escritura sobre el directorio de destino ( transfer.local.directory ) Parámetros El ejecutor tiene tres superficies de configuración: Flags de CLI (esta sección). Parámetros en tiempo de ejecución — args posicionales y params con nombre, pasados después de los flags (esta sección). La plantilla YAML — tareas, agentes/módulos y transferencia. Referencia completa en Referencia de plantillas . Flags de CLI pandora-plugin_exec -t [flags] [args...] [clave=valor...] Flag Efecto -t , --template Plantilla a ejecutar (obligatorio) --dry-run Imprime el XML generado por stdout, no transfiere --strict Aborta en el primer fallo de tarea en lugar de omitir -v , --verbose Registro de depuración en stderr (resultados de tareas, omisiones, avisos) --version Imprime la versión y sale Código de salida Significado 0 Éxito 1 Error de parseo/validación de la plantilla 2 Fallo de tarea bajo --strict 3 Error de transferencia Los flags van antes de los argumentos: el parseo de flags se detiene en el primer token que no es flag, así que los flags deben preceder a los argumentos ( pandora-plugin_exec -t tpl.yml --dry-run sda1 , no ... sda1 --dry-run ). Un argumento que empieza por guion se rechaza con un error claro en lugar de tratarse silenciosamente como posicional. Parámetros en tiempo de ejecución Los tokens que quedan después de los flags alimentan dos variables reservadas, sembradas en el almacén antes de que se ejecute ninguna tarea: args — lista ordenada de tokens posicionales: {{ args[0] }} , {{ len(args) }} , for_each: args . params — mapa de tokens con nombre: {{ params.host }} , {{ params.threshold + 5 }} . Reglas de parseo: Cada token se divide por el primer = sin escapar . Un = inmediatamente precedido por \ es un literal escapado y no divide. El token tiene nombre solo si la clave candidata (tras desescapar \= → = ) coincide con [A-Za-z_][A-Za-z0-9_]* . En caso contrario, todo el token es posicional . Todo token se clasifica — no hay casos de error. Claves con nombre duplicadas: gana la última. Los valores con aspecto numérico se convierten en números, así que la aritmética funciona con ellos. Las variables de tarea no pueden llamarse args ni params (reservadas); for_each: args sí es válido. Trampa del escapado en shell: bash consume \= antes de que el ejecutor lo vea. Protege la barra invertida con comillas simples ( 'query\=a=b' ) o duplícala ( query\\=a=b ). Token Se convierte en sda1 args[0] = "sda1" 90 args[0] = 90 (número) host=db01 params.host = "db01" threshold=90 params.threshold = 90 (número) conn=user=a;pw=b params.conn = "user=a;pw=b" (solo el primer = divide) 'query\=a=b' posicional "query=a=b" (la clave contendría = — no es un identificador) =foo posicional "=foo" (clave vacía) k=1 k=2 params.k = 2 (gana la última) Parámetros opcionales Los parámetros en tiempo de ejecución fallan de forma explícita, no silenciosa: referenciar una variable no definida no renderiza una cadena vacía — el módulo se omite con un aviso, para proteger contra erratas. Por tanto, un {{ params.x }} a secas omite el módulo cuando x no se pasó. Para hacer opcional un parámetro, elige un patrón explícito: Patrón Efecto {{ params.missing ?? "default" }} Renderiza un valor por defecto cuando falta la clave when: "'k' in params" Incluye el módulo solo cuando se pasó la clave {{ len(args) > 1 ? args[1] : "all" }} Protege un acceso posicional según el tamaño Parámetros aceptados por las plantillas incluidas Plantilla Parámetro Por defecto Efecto templates/df_used.yml include — Fuerza monitorizar un montaje cuyo fstype está fuera de la lista por defecto (coincidencias exactas de montaje separadas por comas) templates/df_used.yml exclude — Nunca monitoriza un montaje — exclude siempre gana sobre include templates/pandora_netusage.yml include — Monitoriza solo estas interfaces (nombres exactos separados por comas) templates/pandora_netusage.yml exclude — Nunca monitoriza una interfaz — exclude siempre gana sobre include templates/pandora_mem_used.yml mem_critical 95 Umbral crítico para el porcentaje de Memory_Used templates/pandora_mem_used.yml swap_critical 95 Umbral crítico para el porcentaje de Swap_Used Ejecución manual Formato de ejecución pandora-plugin_exec -t [flags] [args...] [clave=valor...] Ejemplos Ejecuta una plantilla incluida sin enviar nada (el binario pandora-plugin_exec se entrega ya compilado): pandora-plugin_exec -t templates/df_used.yml --dry-run Deberías ver bloques XML en stdout, un par por sistema de archivos montado. Ejecuciones reales con parámetros en tiempo de ejecución: pandora-plugin_exec -t templates/df_used.yml # lista de fstype por defecto pandora-plugin_exec -t templates/df_used.yml exclude=/DB,/snap # descarta dos montajes pandora-plugin_exec -t templates/df_used.yml include=/mnt/zfs # monitoriza también un montaje zfs pandora-plugin_exec -t templates/pandora_netusage.yml # todas las interfaces pandora-plugin_exec -t templates/pandora_netusage.yml exclude=lo,docker0 # descarta loopback y docker0 pandora-plugin_exec -t templates/pandora_netusage.yml include=eth0,ens33 # solo eth0 + ens33 pandora-plugin_exec -t templates/pandora_netusage.yml include=lo exclude=lo # exclude gana → 0 bytes pandora-plugin_exec -t templates/pandora_mem_used.yml # valores por defecto pandora-plugin_exec -t templates/pandora_mem_used.yml mem_critical=90 swap_critical=85 Modo verbose -v / --verbose escribe el registro de depuración en stderr , dejando stdout limpio para el XML generado. Informa de los resultados por tarea (variables almacenadas), módulos omitidos y avisos: pandora-plugin_exec -t templates/df_used.yml --dry-run -v No hay archivo de log ni nivel de log; la salida verbose es un único flujo de stderr. Configuración en Pandora FMS El ejecutor es one-shot : ejecuta la plantilla una vez y termina. La planificación es externa. Escenario Cómo Como plugin de agente La plantilla usa transfer.mode: agent_plugin ; configuración del agente: module_plugin /usr/bin/pandora-plugin_exec -t /etc/pandora/templates/df_used.yml Desde cron / lado servidor La plantilla usa el modo tentacle o local ; crontab: */5 * * * * pandora-plugin_exec -t /etc/pandora/templates/myapp.yml Ejecutándose en el propio servidor de Pandora FMS Modo local apuntando al directorio de datos entrantes ( /var/spool/pandora/data_in ) Depurando una plantilla pandora-plugin_exec -t plantilla.yml --dry-run -v No hay asistente ni paquete .disco para este plugin: la instalación consiste en colocar el binario (y las plantillas) donde el agente/cron pueda alcanzarlos, y luego conectar la línea module_plugin o la entrada de crontab mostradas arriba. Agentes y módulos generados por el plugin Agentes. Cada bloque agents: se convierte en un documento XML (en los modos tentacle / local ). La identidad del agente sale directamente de la plantilla: name , alias , parent_agent_name , description , version , os_name , os_version , timestamp , address , group , interval , agent_mode . En modo agent_plugin no se emite cabecera de agente — solo fragmentos — y con varios bloques agents: cada nombre de módulo se prefija con - (separador configurable mediante transfer.module_prefix_separator ) para que los módulos no colisionen bajo el único agente real. Módulos. Un módulo por bloque de módulo resuelto (un bloque for_each se resuelve en un módulo por elemento). El tipo de módulo es el que declare la plantilla ( generic_data , generic_data_inc , generic_data_string , async_data , ...) — el ejecutor reenvía los campos del módulo al servidor de datos sin modificarlos. La lista completa de campos de módulo admitidos está en Referencia de plantillas . Una tarea fallida (error de ejecución, estado HTTP fuera de 2xx, filtro con 0 coincidencias) deja su variable sin definir y registra un aviso; un módulo que referencia una variable sin definir se omite con un aviso y los módulos restantes se entregan igualmente. --strict convierte cualquiera de lo anterior en un aborto (código de salida 2). Plantillas incluidas El plugin incluye plantillas ya construidas bajo templates/ , divididas en dos grupos. Ejemplos Plantilla Propósito templates/http_api_example.yml Encadenamiento de API HTTP con autenticación por token (login → token → petición autenticada) templates/local_commands_example.yml Comandos locales como plugin de agente, mostrando todas las formas de resultado de regexp Sustitutos de plugins de agente heredados Plantilla Sustituye a templates/df_used.yml pandora_df_used_go — espacio usado del sistema de archivos templates/pandora_mem_used.yml pandora_mem_used — memoria y swap templates/pandora_netusage.yml pandora_netusage — uso de red Referencia de plantillas Una plantilla es un archivo YAML con tres secciones: tasks , agents (o modules de nivel superior) y transfer . El parseo es estricto : un nombre de campo desconocido es un error, así que las erratas fallan rápido. Ejemplo mínimo completo tasks: - name: disk type: local local: { command: "df --output=pcent / | tail -1 | tr -d ' %'" } variable: disk_pct agents: - name: "myhost" group: "Servers" interval: 300 modules: - name: "Disk used" type: generic_data data: "{{ disk_pct }}" unit: "%" transfer: mode: tentacle tentacle: { address: "pandora.example.com" } tasks: — lista ordenada de ejecuciones Las tareas se ejecutan secuencialmente, en el orden declarado . Una tarea puede usar variables producidas por tareas anteriores en cualquiera de sus campos de cadena (encadenamiento: login → token → petición autenticada). Campo Obligatorio Descripción name sí Identificador único de tarea (usado en los logs) type sí request o local request si type=request Especificación de petición HTTP (abajo) local si type=local Especificación de comando (abajo) filter no Filtro de extracción (abajo). Sin él, se almacena la salida bruta recortada variable no Nombre bajo el que almacenar el resultado. Debe coincidir con [A-Za-z_][A-Za-z0-9_]* request: (HTTP) Campo Obligatorio Por defecto Descripción url sí — URL de destino. Admite {{ }} method no GET GET , POST , PUT , DELETE , PATCH , HEAD headers no — Mapa de cabecera → valor. Los valores admiten {{ }} body no — Cuerpo de la petición. Admite {{ }} timeout no 30 Segundos skip_tls_verify no false Acepta certificados TLS inválidos Una respuesta con estado fuera de 2xx es un fallo de tarea. Los cuerpos de respuesta están limitados a 10 MiB. local: (comando) Campo Obligatorio Por defecto Descripción command sí — Se ejecuta con /bin/sh -c . Admite {{ }} . El $ de shell no se toca ( awk '{print $5}' funciona tal cual) timeout no 30 Segundos; el proceso se mata al expirar Un código de salida distinto de cero es un fallo de tarea (stderr se incluye en el log). filter: — extracción de valores Campo Obligatorio Descripción type sí jq o regexp expression sí La expresión del filtro Cardinalidad del resultado (ambos tipos de filtro): 0 coincidencias → variable sin definir (semántica de fallo de tarea) · 1 coincidencia → escalar · N coincidencias → array. jq — la salida de la tarea debe ser JSON válido. La expresión se ejecuta con un motor jq incorporado (no se necesita el binario jq ). Cada valor que emite la expresión es un elemento: .token → escalar, .items[].name → array, .items[] → array de objetos. regexp — sintaxis RE2 de Go (sin backtracking, sin lookahead) aplicada a la salida bruta. Por coincidencia: Forma del patrón Cada coincidencia se convierte en Sin grupo de captura ( cpu\d ) La coincidencia completa (cadena) Un grupo sin nombre ( (\d+)% ) El grupo de captura 1 (cadena) 2+ grupos sin nombre ( (a)(b)(c) ) Array posicional: [[a, b, c], ...] Grupos con nombre ( (?P...) ) Un objeto: nombre del grupo → texto capturado Usa (?m) para patrones multilínea anclados por línea y (?i) para coincidencia sin distinción de mayúsculas. No mezcles grupos con nombre y sin nombre en la misma regexp si quieres un acceso predecible; la forma con nombre solo expone los campos con nombre. Coerción de tipos: las cadenas extraídas con aspecto numérico se convierten en números (recursivamente, incluyendo campos de objetos), así que la aritmética funciona con ellas. Variables y expresiones {{ }} Cualquier campo de cadena de tareas (después de la tarea que la produce), agentes y módulos puede incluir {{ expresión }} . Las expresiones se evalúan con expr-lang contra el almacén de variables. Expresión Resultado {{ token }} Valor de la variable {{ 100 - disk_pct }} Aritmética {{ names[0] }} Indexado de array {{ value.mount }} Campo de objeto (dentro de for_each ) {{ names[index] }} Array paralelo sincronizado (dentro de for_each ) Referenciar una variable no definida es un error → el módulo afectado se omite (o la ejecución aborta bajo --strict ). agents: — un bloque por agente Obligatorio a menos que uses modules: de nivel superior (ver el modo agent_plugin ). Cada agente se convierte en un documento XML . Campo Obligatorio Descripción name sí Nombre del agente modules sí Lista de bloques de módulo (al menos uno) alias no Alias del agente parent_agent_name no Agente padre description no Descripción version no Cadena de versión del agente os_name , os_version no Identificación del SO timestamp no Sobrescribe la marca de tiempo de los datos address no IP/nombre de host group no Grupo de destino interval no Segundos (entero) agent_mode no Modo del agente Todos los campos de cadena admiten {{ }} . modules: — bloques de módulo Los bloques de módulo viven dentro de un agente, o a nivel superior (solo con transfer.mode: agent_plugin ). Campos de control (semántica del ejecutor, no se envían a Pandora): Campo Descripción for_each Nombre de variable array. El bloque se expande a un módulo por elemento ; {{ value }} e {{ index }} quedan disponibles. Una variable escalar itera como array de 1 elemento. Para grupos de captura sin nombre con 2+ grupos, cada value es un array, así que puedes usar {{ value[0] }} . ¿Varios módulos por elemento? Escribe varios bloques con el mismo for_each when Expresión booleana expr-lang en bruto (sin {{ }} ). Falso → módulo (o elemento) omitido. Dentro de for_each se evalúa por elemento y ve value / index . Ejemplo: when: 'not (value.mount matches "^/DB")' Campos de datos — los dos obligatorios más el conjunto completo aceptado por el servidor de datos. Los valores pueden ser números o cadenas en YAML; todos admiten {{ }} : Campo Obligatorio Descripción name sí Nombre del módulo type sí Tipo de módulo ( generic_data , generic_proc , generic_data_string , async_data , ...) data no Valor del módulo description no Descripción unit no Etiqueta de unidad interval no Intervalo del módulo tags no Etiquetas module_group no Grupo de módulo module_parent , module_parent_unlink no Relación de módulo padre min_warning , max_warning , min_critical , max_critical no Umbrales numéricos min_warning_forced , max_warning_forced , min_critical_forced , max_critical_forced no Variantes de umbral forzado str_warning , str_critical no Umbrales por coincidencia de cadena str_warning_forced , str_critical_forced no Umbrales de cadena forzados warning_inverse , critical_inverse no Invertir la lógica del umbral min , max no Rango de datos válido post_process no Multiplicador aplicado por el servidor disabled no Crear deshabilitado status no Forzar estado timestamp no Sobrescribe la marca de tiempo de los datos custom_id no Identificador personalizado critical_instructions , warning_instructions , unknown_instructions no Instrucciones del operador quiet no Modo silencioso min_ff_event , min_ff_event_normal , min_ff_event_warning , min_ff_event_critical no Umbrales FlipFlop module_ff_interval , ff_type , ff_timeout , each_ff no Comportamiento FlipFlop crontab no Planificación de módulo estilo cron extra_data no Carga extra alert_templates no Lista de nombres de plantillas de alerta a vincular transfer: — entrega Campo Obligatorio Descripción mode sí tentacle , local o agent_plugin tentacle.address para tentacle Dirección del servidor Pandora tentacle.port no (por defecto 41121 ) Puerto tentacle tentacle.binary no Ruta a tentacle_client si no está en $PATH tentacle.extra_args no Argumentos extra del cliente tentacle (lista) local.directory para local Directorio donde se escribe el archivo XML .data module_prefix_separator no (por defecto " - " ) Separador para el prefijado multi-agente en modo agent_plugin Modo Salida Caso de uso tentacle XML completo enviado vía cliente tentacle Ejecución remota (cron, Discovery) local XML completo escrito en un directorio Ejecutarse en el propio servidor Pandora (apuntando al directorio de entrada), o depuración agent_plugin Solo fragmentos impresos en stdout — el agente real añade la cabecera Ejecutarse como module_plugin de un agente de software Particularidades de agent_plugin : Se permite modules: de nivel superior sin ningún bloque agents: (y solo se permite en este modo). Con varios agentes definidos, cada nombre de módulo se prefija con - para que los módulos no colisionen bajo el único agente real. Crear tus propias plantillas Una plantilla es solo YAML. Constrúyela de arriba a abajo: decide qué medir (tareas), cómo extraer los números (filtros), qué módulos exponer (agentes/módulos) y cómo entregar (transferencia). Paso 1 — Parte de un ejemplo incluido templates/local_commands_example.yml es la plantilla completa más corta y demuestra las tres formas de resultado de regexp. Cópiala y sustituye las tareas: cp templates/local_commands_example.yml templates/my_template.yml Paso 2 — Escribe las tareas Cada tarea ejecuta un comando o una petición HTTP y almacena su salida (opcionalmente filtrada) en una variable. tasks: # Un escalar: un valor, sin filtro (se almacena la salida bruta recortada). - name: uptime_seconds type: local local: { command: "awk '{print int($1)}' /proc/uptime" } variable: uptime # Un escalar mediante una regexp con una sola captura. - name: load_avg type: local local: { command: "cat /proc/loadavg" } filter: type: regexp expression: '^(\d+\.\d+)' variable: load1 # Un array de objetos mediante grupos de captura con nombre. - name: df type: local local: { command: "df -kTP" } filter: type: regexp expression: '(?m)^(?P\S+)\s+(?P\S+)\s+(?P\d+)\s+(?P\d+)\s+\d+\s+(?P\d+)%\s+(?P.+)$' variable: filesystems Paso 3 — Convierte variables en módulos Una variable escalar alimenta directamente un módulo; un array alimenta bloques for_each (un módulo por elemento). modules: - name: "Uptime seconds" type: generic_data data: "{{ uptime }}" - name: "Load average 1m" type: generic_data data: "{{ load1 }}" # Un módulo por sistema de archivos; value.* lee los grupos de captura con nombre. - for_each: filesystems when: 'value.fstype != "tmpfs"' name: "DiskUsed_{{ value.mount }}" type: generic_data data: "{{ value.pct }}" unit: "%" Dentro de un bloque for_each : {{ value }} es el elemento actual, {{ index }} su posición (base 0). Los grupos con nombre hacen que cada elemento sea un objeto → accede a los campos como {{ value.mount }} . 2+ grupos sin nombre hacen que cada elemento sea un array posicional → accede como {{ value[0] }} , {{ value[1] }} , ...; escribe varios bloques con el mismo for_each para emitir varios módulos por elemento. Paso 4 — Elige el modo de transferencia transfer: mode: agent_plugin # salida solo de módulos; ejecutar como plugin de agente de software transfer: mode: tentacle # XML de agente completo a un servidor Pandora tentacle: { address: "pandora.example.com" } transfer: mode: local # XML de agente completo escrito en un directorio local: { directory: "/var/spool/pandora/data_in" } Paso 5 — Valida antes de entregar pandora-plugin_exec -t templates/my_template.yml --dry-run -v --dry-run imprime el XML generado sin transferir; -v muestra qué variables se almacenaron y qué módulos se omitieron. El parser es estricto, así que cualquier errata en un nombre de campo falla inmediatamente. Errores comunes Los flags después de los argumentos no funcionan. El parseo de flags se detiene en el primer token que no es flag: pon --dry-run antes de cualquier argumento posicional. Las variables no definidas omiten el módulo , no renderizan vacío. Usa {{ params.x ?? "default" }} o when: "'x' in params" para parámetros opcionales. filter.expression no es expr. Es jq o una regexp RE2 según filter.type . expr solo se usa dentro de {{ }} y en when: . Duplica la barra invertida en literales regexp dentro de when: — los literales de cadena de expr procesan escapes, así que \\d entrega \d al motor de regexp. RE2 no tiene lookahead/backtracking — usa when: para exclusiones (p. ej. when: 'not (value.mount matches "^/DB")' ). Referencia rápida del lenguaje de expresiones Los placeholders {{ }} y las condiciones when: son expresiones expr-lang . Funciones útiles para autores de plantillas: Categoría Funciones / operadores Cadenas split(s, sep) · join(list, sep) · upper(s) · lower(s) · trim(s) · replace(s, old, new) · contains(s, sub) · startsWith(s, pre) · endsWith(s, suf) · s matches "patrón-re2" Colecciones x in list · len(list) · list[i] · first(list) · last(list) · filter(list, pred) · map(list, expr) · all / any / none(list, pred) · sort(list) · uniq(list) · concat(a, b) Lógica y nil a ?? "default" (coalescencia de nil) · obj?.field (encadenamiento opcional) · cond ? a : b (ternario) · 'clave' in map (presencia de clave) Números + - * / % · abs(x) · min / max(a, b) · sum(list) · avg(list) · int(x) · float(x) · string(x) JSON toJSON(x) · fromJSON(s) En los predicados de filter / map / all / any / none el elemento actual es # (p. ej. filter(filesystems, #.pct > 90) ). No confundas lenguajes: el filter.expression de la tarea no es expr — es jq (vía gojq) o una regexp RE2, según filter.type . expr solo se usa dentro de {{ }} y en when: . Ejemplo práctico: sustituir un plugin real templates/df_used.yml sustituye al plugin de agente pandora_df_used_go . El patrón a aprender: una ejecución → array de objetos → N módulos . tasks: - name: df type: local local: { command: "df -kTP" } filter: type: regexp # Grupos con nombre: cada fila de df se convierte en {fs, fstype, total, used, pct, mount} expression: '(?m)^(?P\S+)\s+(?P\S+)\s+(?P\d+)\s+(?P\d+)\s+\d+\s+(?P\d+)%\s+(?P.+)$' variable: filesystems modules: - for_each: filesystems when: > (value.fstype matches "^(?i:adfs|affs|autofs|btrfs|cifs|coda|coherent|efs|ext\\d?|hfs|hfsplus|hpfs|jfs|minix|msdos|ncpfs|nfs4?|ntfs|proc|qnx4|reiserfs|smbfs|sysv|ubifs|udf|ufs|umsdos|usbfs|vfat|xenix|xfs|xiafs)$" || value.mount in split(params.include ?? "", ",")) && not (value.mount in split(params.exclude ?? "", ",")) name: "DiskUsed_{{ value.mount }}" type: generic_data data: "{{ value.pct }}" unit: "%" - for_each: filesystems when: > (value.fstype matches "^(?i:adfs|affs|autofs|btrfs|cifs|coda|coherent|efs|ext\\d?|hfs|hfsplus|hpfs|jfs|minix|msdos|ncpfs|nfs4?|ntfs|proc|qnx4|reiserfs|smbfs|sysv|ubifs|udf|ufs|umsdos|usbfs|vfat|xenix|xfs|xiafs)$" || value.mount in split(params.include ?? "", ",")) && not (value.mount in split(params.exclude ?? "", ",")) name: "DiskUsed_{{ value.mount }} bytes" type: generic_data data: "{{ value.used }}" unit: "bytes" transfer: mode: agent_plugin La regexp captura todas las filas de df; la lista permitida de fstype por defecto vive en when: para que los parámetros en tiempo de ejecución puedan sobrescribirla. Observa el escalar plegado ( when: > ) y la barra invertida duplicada ( \\d ): los literales de cadena de expr procesan escapes, así que \\d entrega un \d literal al motor de regexp. Ejemplo de capturas posicionales Úsalo cuando quieras el orden de captura en lugar de nombres de campo: tasks: - name: fs type: local local: command: | printf '/dev/sda1 ext4 40 81 /\n/dev/sdb1 xfs 90 12 /data\n' filter: type: regexp expression: '(?m)^(\S+)\s+(\S+)\s+(\d+)\s+(\d+)\s+(.+)$' variable: filesystems modules: - for_each: filesystems name: "FS {{ value[0] }}" type: generic_data data: "{{ value[3] }}" unit: "%" transfer: mode: agent_plugin Ejemplo de API HTTP templates/http_api_example.yml demuestra el encadenamiento con autenticación por token: una petición login almacena el token de sesión, y las peticiones posteriores lo incorporan mediante Authorization: "Bearer {{ token }}" . Lista de verificación de plantillas (para humanos y generadores de IA) Cada tarea tiene un name único y su bloque coincide con su type ( request: xor local: ). Cada nombre de variable coincide con [A-Za-z_][A-Za-z0-9_]* (y no es args / params ). Las tareas están ordenadas de modo que las variables se producen antes de consumirse. O bien agents: (con ≥1 módulo cada uno) o modules: de nivel superior + modo agent_plugin — nunca ambos. Cada módulo tiene name y type . for_each referencia una variable que un filtro produce como array; los campos de elemento se acceden como {{ value.field }} para capturas con nombre o {{ value[0] }} para capturas posicionales. when: es una expresión en bruto (sin {{ }} ); todo lo demás usa placeholders {{ }} . transfer.mode tiene sus opciones obligatorias ( tentacle.address / local.directory ). Validado con pandora-plugin_exec -t plantilla.yml --dry-run -v antes de entregar.