我们这边有个需求,虽然 Zabbix-Agent 可以获取所有分区的空间利用率的监控项,但是我们需要将服务器的所有数据对接到上游的监管系统,监管平台的厂商说为了确保唯一性,我们提供给他们的数据一定要有所有分区的 UUID。这可把我整蒙了,他们不能自己生成一个 UUID 嘛?这个需求也是冷门需求反正我谷歌也没类似的案例,只能靠自己了。

不使用自定义监控项的方法

经过我占用了 3 小时工时的研究下,在不做自定义监控项且不运行系统命令 (system.run 监控项是默认关闭也不可能开启的) 的情况下,只能读取 /etc/fstab 分区的 UUID 和挂载位置了。

我曾经想利用 vfs.dir.get["/dev/disk/by-uuid/"] 获取这个目录的数据,因为可以输出磁盘的 UUID 和软链接的最终位置,但是在 Zabbix 输出的结果是只有 UUID,软链接的路径不会输出,不符合我的需求。如果要输出的话还是得自定义监控项。

但是 /etc/fstab 看 UUID 和挂载位置有个问题,就是挂载的磁盘不一定只能用 UUID 挂载,只是 UUID 的唯一性好很多,有时候用 LVM 的路径挂载也可以挂载到。所以说这个方法只适用于有规范性操作指引且用户不用 LVM 卷只能标准分区。因为我只要输出 json 就行了,如果要用 LLD 自动发现的话,记得用 相关项目 关联监控项哦。

  • 键值: vfs.file.contents["/etc/fstab"]
  • 预处理选 Javascript,代码这样写(代码我是跟 GPT 扯了半小时生成的)
const fstabContent = value;
// 正则表达式用于匹配 UUID 和分区信息
const uuidPattern = /UUID=("|')?([^"'\s]+)\1\s+(\S+)\s+/g;

// 匹配所有符合条件的结果
var matches = [];
var match;

while ((match = uuidPattern.exec(fstabContent)) !== null) {
  matches.push({
    UUID: match[2],
    Partition: match[3]
  });
}

return JSON.stringify(matches, null, 0);

测试结果

测试是成功的,正则对用了双引号或单引号整了点容错性。

如果主机的分区都是 LVM 的话,那就歇菜了。不适用这个方法了。

使用自定义监控项

所以说,这也太将就了,到时候万一都用 LVM 卷了我这个岂不是完了。自定义监控项玩法就多了。系统还可以看分区的 UUID 的命令还有 blkidlsblk,我看了下还是选择 lsblk 作为自定义监控项吧。但是有个问题,我用麒麟和统信和 CentOS,输出结果字段是不同的。查看就是不同的内核版本的 util-linux 组件的版本也不同(不对啊,我阿里的是5.10,util组件是2.32;麒麟的是 4.19,组件是 2.35)。lsblk 使用 --pairs 可以以 key-value 的形式输出,新版可以用 json 输出,但是大家也知道什么叫 新版。新版的好处还可以输出空间占用率。

我们先不管这个问题,反正有 UUID 就行了。自定义的脚本这样写。自定义监控项的设置自己百度吧,都做监控系统了不要想着一篇文章从开天辟地开始教吧。

#!/bin/bash

lsblk_output=$(lsblk -af --pairs)
echo "$lsblk_output"

然后监控项的话,这样设置,信息类型选文本

预处理的 Javascript 的代码如下,也是我跟 GPT 扯了一小时才可以。跟 GPT 讨论监控项的 js 预处理代码,一定要这样跟 GPT 说好,不然试错几次很烦。(我的 Javascript 环境是基于 Duktape,不支持 let 和 Object.assign 这些函数,需要遍历的地方尽可能用 for。

const lines = value.trim().split('\n');
var formattedOutput = {};

for (var i = 0; i < lines.length; i++) {
  var parts = lines[i].split(' ');

  // Ensure the line has at least 5 parts before attempting to access them
  if (parts.length >= 5) {
    var deviceName = parts[0].replace('NAME="', '').replace('"', '');
    var fstype = parts[1].replace('FSTYPE="', '').replace('"', '');

    var fsver = '';
    var label = '';
    var uuid = '';
    var fsavail = '';
    var fsusePercent = '';
    var mountpoint = '';

    for (var j = 2; j < parts.length; j++) {
      var keyValue = parts[j].split('=');
      var key = keyValue[0];
      var value = keyValue[1] ? keyValue[1].replace(/"/g, '') : '';

      switch (key) {
        case 'FSVER':
          fsver = value;
          break;
        case 'LABEL':
          label = value;
          break;
        case 'UUID':
          uuid = value;
          break;
        case 'FSAVAIL':
          fsavail = value;
          break;
        case 'FSUSE%':
          fsusePercent = value;
          break;
        case 'MOUNTPOINT':
          mountpoint = value;
          break;
      }
    }

    // Manually merge attributes without using Object.assign
    var mergedAttributes = {
      fstype: fstype,
      fsver: fsver,
      label: label,
      uuid: uuid,
      fsavail: fsavail,
      fsusePercent: fsusePercent,
      mountpoint: mountpoint,
    };

    formattedOutput[deviceName] = mergedAttributes;
  }
}

// Convert formatted output to JSON
var jsonOutput = JSON.stringify(formattedOutput, null, 0);
return jsonOutput;

上面的代码也是有容错性,兼容我上面截图的两种输出情况。输出结果如下,如果 lsblk 是旧版的话,只是很多字段都空出来了,该有的值都有的。

如果后面还有需求看 VG 卷或 LV 卷的 UUID 需求,哎再说吧。