我们使用 JavaScript 解决 Zabbix 中的实际问题

我们使用 JavaScript 解决 Zabbix 中的实际问题
吉洪·乌斯科夫、Zabbix集成团队工程师

Zabbix是一个可定制的平台,用于监控任何类型的数据。 从 Zabbix 的最早版本开始,监控管理员就能够通过以下方式运行各种脚本: 行动 用于检查目标网络节点。 同时,脚本的推出也带来了许多困难,包括需要支持脚本、将脚本交付到通信节点和代理、以及对不同版本的支持等。

用于 Zabbix 的 JavaScript

2019 年 4.2 月,Zabbix 3.4 推出,带有 JavaScript 预处理功能。 许多人对放弃编写脚本的想法感到兴奋,这些脚本将数据带到某处,消化它并以 Zabbix 理解的格式提供它,并执行简单的检查,以接收尚未准备好由 Zabbix 存储和处理的数据,以及然后使用 Zabbix 和 JavaScript 工具处理该数据流。 结合 Zabbix XNUMX 中出现的低级发现和依赖项,我们得到了一个相当灵活的概念来排序和管理接收到的数据。

在Zabbix 4.4中,作为JavaScript中预处理的逻辑延续,出现了一种新的通知方法——Webhook,可以使用它轻松地将Zabbix通知与第三方应用程序集成。

JavaScript 和 Duktapes

为什么选择 JavaScript 和 Duktape? 考虑了语言和引擎的各种选项:

  • 卢阿 - 卢亚 5.1
  • Lua-LuaJIT
  • Javascript - Duktape
  • JavaScript——JerryScript
  • 嵌入式Python
  • 嵌入式 Perl

主要选择标准是流行性、引擎与产品集成的便捷性、引擎的低资源消耗和整体性能,以及将这种语言的代码引入监控的安全性。 根据指标组合,JavaScript 在 Duktape 引擎上获胜。

我们使用 JavaScript 解决 Zabbix 中的实际问题

选择标准和性能测试

杜克胶带特点:

- 标准 ECMAScript E5/E5.1
- Duktape 的 Zabbix 模块:

  • Zabbix.log() - 允许您将不同详细级别的消息直接写入 Zabbix Server 日志中,这使得将 Webhook 中的错误与服务器状态关联起来成为可能。
  • CurlHttpRequest() - 允许您向网络发出 HTTP 请求,Webhook 的使用就是基于此。
  • atob() 和 btoa() - 允许您以 Base64 格式对字符串进行编码和解码。

注意. Duktape 符合 ACME 标准。 Zabbix使用2015版本的脚本。 后续更改很小,因此可以忽略。.

JavaScript 魔法

JavaScript 的所有魔力都在于动态类型和类型转换:字符串、数字和布尔值。

这意味着无需提前声明变量应返回值的类型。

在数学运算中,函数运算符返回的值被转换为数字。 此类操作的例外是加法,因为如果至少其中一个术语是字符串,则字符串转换将应用于所有术语。

注意. 负责此类转换的方法通常在对象的父原型中实现, 的价值 и 字符串. 的价值 在数值转换期间调用,并且始终在方法之前调用 字符串. 方法 的价值 必须返回原始值,否则其结果将被忽略。

在对象上调用方法 的价值。 如果没有找到或者没有返回原始值,则调用该方法 字符串。 如果方法 字符串 没有找到,正在寻找 的价值 在对象的原型中,重复一切,直到值的处理完成并且表达式中的所有值都转换为相同类型. 如果对象实现了一个方法 字符串,它返回一个原始值,那么它就是用于字符串转换的。 但是,应用此方法的结果不一定是字符串。

例如,如果 for for 对象 'OBJ' 方法已定义 字符串,

`var obj = { toString() { return "200" }}` 

方法 字符串 准确返回一个字符串,当将字符串与数字相加时,我们得到一个粘合字符串:

`obj + 1 // '2001'` 

`obj + 'a' // ‘200a'`

但如果你重写 字符串,使得该方法返回一个数字,当对象相加时,会进行数值转换的数学运算,得到数学相加的结果。

`var obj = { toString() { return 200 }}` 

`obj + 1 // '2001'`

在这种情况下,如果我们对字符串执行加法,则会执行字符串转换,并得到一个粘合字符串。

`obj + 'a' // ‘200a'`

这就是 JavaScript 新手出现大量错误的原因。

方法 字符串 您可以编写一个函数,将对象的当前值加 1。

我们使用 JavaScript 解决 Zabbix 中的实际问题
执行脚本,假设变量等于3,它也等于4。

与强制转换(==)相比,该方法每次都会执行 字符串 具有增值功能。 因此,随着每次后续比较,该值都会增加。 这可以通过使用非转换比较 (===) 来避免。

我们使用 JavaScript 解决 Zabbix 中的实际问题
没有类型转换的比较

注意. 不必要时不要使用演员比较.

对于复杂的脚本,例如逻辑复杂的Webhook,需要进行类型转换的比较,建议预先编写对返回变量的值的检查并处理不一致和错误。

网络钩子媒体

在 2019 年底和 2020 年初,Zabbix 集成团队一直在积极开发 Zabbix 发行版附带的 Webhooks 和开箱即用集成。

我们使用 JavaScript 解决 Zabbix 中的实际问题
链接到 文件

前处理

  • JavaScript 中预处理的出现使得放弃大多数外部脚本成为可能,目前在 Zabbix 中您可以获取任何值并将其转换为完全不同的值。
  • Zabbix中的预处理是通过JavaScript代码实现的,当编译成字节码时,它被转换成一个以单个值作为参数的函数 折扣值 作为字符串(字符串可以包含数字和数字)。
  • 由于输出是一个函数,因此在脚本末尾需要 回报.
  • 可以在代码中使用自定义宏。
  • 资源不仅可以在操作系统级别进行限制,还可以通过编程方式进行限制。 预处理步骤最多分配 10 MB RAM,运行时间限制为 10 秒。

我们使用 JavaScript 解决 Zabbix 中的实际问题

注意. 10 秒的超时值相当大,因为根据相当“重”的预处理场景,在 1 秒内收集条件数千个数据项可能会减慢 Zabbix 的速度。 因此,不建议使用预处理通过所谓的影子数据元素(虚拟项)执行完整的 JavaScript 脚本,这些元素仅运行以执行预处理.

您可以通过预处理测试或使用实用程序检查您的代码 zabbix_js:

`zabbix_js -s *script-file -p *input-param* [-l log-level] [-t timeout]`

`zabbix_js -s script-file -i input-file [-l log-level] [-t timeout]`

`zabbix_js -h`

`zabbix_js -V`

实际任务

任务 1

用预处理替换计算项。

条件:从传感器获取华氏温度并以摄氏度存储。

以前,我们将创建一个收集华氏温度的项目。 之后,另一个数据项(计算)将使用公式将华氏温度转换为摄氏度。

问题:

  • 需要复制数据元素并将所有值存储在数据库中。
  • 您必须就公式中计算和使用的“父”数据项以及计算的数据项的间隔达成一致。 否则,计算项可能会进入不受支持的状态或计算出先前的值,从而影响监测结果的可靠性。

一种解决方案是放弃灵活的检查间隔,转而采用固定间隔,以确保在接收数据的项目(在我们的示例中为华氏温度)之后评估计算项目。

但例如,如果我们使用模板检查大量设备,并且每30秒检查一次,Zabbix“黑客”了29秒,并在最后一秒开始检查和计算。 这会创建队列并影响性能。 因此,建议仅在确实必要时才使用固定间隔。

在这个问题中,最佳解决方案是一行 JavaScript 预处理,将华氏度转换为摄氏度:

`return (value - 32) * 5 / 9;`

它快速而简单,您不需要创建不必要的数据项并保留它们的历史记录,并且您还可以使用灵活的时间间隔进行检查。

我们使用 JavaScript 解决 Zabbix 中的实际问题

`return (parseInt(value) + parseInt("{$EXAMPLE.MACRO}"));`

但是,如果在假设的情况下需要添加接收到的数据元素,例如,使用宏中定义的任何常量,则必须考虑到参数 折扣值 展开为字符串。 在字符串加法运算中,两个字符串简单地合并为一个。

我们使用 JavaScript 解决 Zabbix 中的实际问题

`return (value + "{$EXAMPLE.MACRO}");`

要获得数学运算的结果,需要将获得的值的类型转换为数值格式。 为此,您可以使用该功能 解析整数(),它产生一个整数,一个函数 解析浮动(),它产生一个小数,或一个函数 ,它返回一个整数或小数。

2任务

获取证书结束前的时间(以秒为单位)。

条件:服务以“Feb 12 12:33:56 2022 GMT”格式颁发证书到期日期。

在 ECMAScript5 中 Date.parse() 接受 ISO 8601 格式 (YYYY-MM-DDTHH:mm:ss.sssZ) 的日期。 必须以 MMM DD YYYY HH:mm:ss ZZ 格式将字符串转换为它

问题:月份值以文本形式表示,而不是数字。 Duktape 不接受这种格式的数据。

解决方案示例:

  • 首先,声明一个带有值的变量(整个脚本是用逗号分隔列出的变量的声明)。

  • 在第一行我们获取参数中的日期 折扣值 并使用以下方法用空格分隔 分裂。 因此,我们得到一个数组,其中数组的每个元素(从索引 0 开始)对应于空格前后的一个日期元素。 分割(0) - 月, 分割(1) - 数字, 分割(2) - 带有时间等的字符串。之后,可以通过数组中的索引来访问日期的每个元素。

`var split = value.split(' '),`

  • 每个月(按时间顺序)对应于其在数组中的位置索引(从 0 到 11)。 要将文本值转换为数值,请将月份索引加一(因为月份从 1 开始编号)。 在这种情况下,加一的表达式放在括号中,因为否则将获得字符串,而不是数字。 最后我们做 片() - 从末尾剪切数组,只留下两个字符(这对于具有两位数的月份很重要)。

`MONTHS_LIST = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],`

`month_index = ('0' + (MONTHS_LIST.indexOf(split[0]) + 1)).slice(-2),`

  • 我们通过按适当的顺序添加字符串,从获得的值形成 ISO 格式的字符串。

`ISOdate = split[3] + '-' + month_index + '-' + split[1] + 'T' + split[2],`

生成的格式中的数据是从 1970 年到未来某个时间点的秒数。 在触发器中使用接收到的格式的数据几乎是不可能的,因为Zabbix只允许你用宏来操作 {日期} и {时间},以用户友好的格式返回日期和时间。

  • 然后,我们可以获取 JavaScript 中 Unix 时间戳格式的当前日期,并从生成的证书到期日期中减去它,以获得从现在到证书到期的毫秒数。

`now = Date.now();`

  • 在 Zabbix 中,我们将接收到的值除以千来得到秒数。

`return parseInt((Date.parse(ISOdate) - now) / 1000);`

在触发器中,您可以指定表达式 '最后的' 后跟一组数字,对应于您要响应的时间段内的秒数,例如以周为单位。 因此,触发器将通知证书将在一周后到期。

注意. 注意使用 解析整数() 在功能上 回报将毫秒除法得到的小数转换为整数。 您还可以使用 解析浮动() 并存储小数数据.

观看报道

来源: habr.com

添加评论