20. 组件

概览

Zabbix支持通过添加第三方组件或自研的组件来增强Zabbix前端的功能,而无需更改Zabbix的源代码。

需要注意的是组件代码将被赋予与Zabbix源代码相同的权限运行。这意味着:

  • 第三方组件可能对zabbix环境产生损害。您必须安装可信任的组件;

  • 第三方组件代码中的错误可能会导致前端崩溃。如果发生这种情况,只需从前端删除组件代码即可。当Zabbix前端重新加载完成后,您将看到一行表明组件被禁用的注释。在Module administrationadministrationgeneralmodules)中再次点击Scan directory可以从数据库中删除不生效的组件。

安装

请务必阅读特定组件的安装手册。建议逐个安装新的组件以便于捕捉安装过程中的失败信息。

在安装组件之前:

  • 应确保所有下载的组件是从可信来源获取的,否则安装带有恶意代码的组件可能会导致数据丢失等后果。

  • 不同版本的同一组件(相同ID)允许并行安装,但同一时间只允许启用单一版本的组件。

组件安装步骤:

  • 将您的组件解包至自己的文件夹中,放入Zabbix前端modules文件夹下。

  • 应确保您的组件文件夹中至少包含manifest.json文件。

  • 前往Module administration并单击Scan directory 按钮。

  • 新的组件将与其版本、作者、描述和状态一起出现在组件列表中。

  • 单击其状态启用组件。

故障排除:

故障解决方法
组件在列表中不可见确保在Zabbix前端modules/your-module/文件夹中包含manifest.json配置文件,若上述条件已满足,那意味着组件版本与Zabbix版本存在冲突。若在 modules/your-module/中找不到manifest.json配置文件,可能是由于您在错误的目录中进行解包导致。
前端崩溃可能是由于组件代码与当前Zabbix版本或服务器配置不兼容导致,请删除这些组件文件并重新加载Zabbix前端,您将看到这些组件已被禁用。前往Module administration并再次单击Scan directory​来移除这些无效组件。 ​
出现相同的命名、ID或操作的错误信息新组件会尝试去注册一个新的组件名,组件ID或操作,如果被已有组件占用,在启用新组件之前,请禁用这些冲突组件(错误信息中已提到)。
​出现其他技术错误提示请将这些组件的报错信息反馈至开发者。 ​

组件开发

组件是基于PHP语言编写的,建议使用Zabbix模型-视图-控制器(MVC)软件设计模式,好处在于其同时也应用于Zabbix前端,有利于后续开发。我们推崇使用PHP严格模式进行开发,但这并非强制性的要求。

请注意,您可以通过组件功能轻松地在Zabbix前端中添加新的菜单项以及对应各自的视图和操作,但暂时无法通过组件功能注册新的API或创建新的数据库表。

组件结构

每个组件都包含一个单独的目录(位于​modules​目录中),其子目录包含控制器、视图和任何其他代码:

  1. example_module_directory/ (必须)
  2. manifest.json (必须) 元数据和操作定义。
  3. Module.php 组件初始化和事件处理。
  4. actions/ 操作控制器文件。
  5. SomethingView.php
  6. SomethingCreate.php
  7. SomethingDelete.php
  8. data_export/
  9. ExportAsXml.php
  10. ExportAsExcel.php
  11. views/ 视图文件。
  12. example.something.view.php
  13. example.something.delete.php
  14. js/ 使用在视图中的JavaScript文件。
  15. example.something.view.js.php
  16. partials/ 视图局部文件。
  17. example.something.reusable.php
  18. js/ 使用在局部中的JavaScript文件。
  19. example.something.reusable.js.php

显而易见,自定义组件目录中唯一的强制要求需包含的配置文件是​manifest.json​,没有此配置文件,组件将无法注册。​Module.php​负责注册菜单项并处理诸如“onBeforeAction”和“onTerminate”之类的事件。​actions​views​和​partials​目录中包含组件操作所需的PHP和JavaScript代码。

命名约定

在创建组件之前,首当其冲的是要统一不同组件项之间(如组件目录和文件)的命名约定,这样我们就可以使这些组件项的名称井然有序,通俗易懂。您也可以在 module_structure找到示例。

组件项命名规则示例
组件目录小写[a-z]、下划线加十进制数字 ​example_v2 ​
操作子目录小写[a-z]、下划线加字符data_export ​
操作文件驼峰式命名法,以操作类型作为结尾SomethingView.php ​
视图和局部文件小写[a-z]
单词用点进行分隔
​module.​作为前缀,后接组件名称
以操作类型和.php文件扩展名作结尾。 ​
module.example.something.view.php ​
Javascript文件除结尾文件扩展名为’​.js.php’​之外,与’​视图和局部文件’​项的命名规则保持一致。 ​module.example.something.view.js.php ​

请注意,’​module’​前缀对于视图和部分文件名是必需的,除非您需要覆盖Zabbix核心视图或部分文件,该条规则不适用于操作文件命名。

Manifest配置文件准备

每个组件都需要一个包含以下JSON格式的Manifest.json配置文件

参数是否必填类型默认描述
manifest_versionDouble-manifest当前版本,当前可支持的版本号为1
idString​ -组件ID,同一ID的组件在同一时间内仅允许启用一个。
nameString-在管理菜单中展示的组件名称。
versionString-在管理菜单中展示的组件版本。
namespaceString-Module.php和操作类的PHP命名规则。
authorString“”​管理菜单中展示的组件作者。
urlString“”​管理菜单中展示的组件URL。
descriptionString“”​管理菜单中展示的组件描述。
actionsObject{}在组件中注册的操作,详情参见“操作”。
configObject{}组件配置。

详情可参见 reference中manifest.json配置文件说明的部分。

操作

组件会对manifest.json配置文件里​actions​对象定义的前端操作进行控制,使用这个方法可以定义新的操作,同样可以对已有操作进行定义。每个操作的键值都应该表示操作名称,相应的值应该包含​class​键和可选的​layout​键和​view​键。

一个操作由四个对应项定义:名称、控制器、视图和布局。数据验证和数据准备工作通常在控制器中完成,格式化输出在视图或局部视图中完成,布局则主要负责用菜单、页眉、页脚等元素装饰前端页面。

组件操作必须在manifest.json配置文件的​actions​对象中进行定义。

参数是否必填类型默认描述
keyString-操作名称。小写[a-z],单词之间用点进行分隔
classString-操作的类名称。包含​actions​目录中的子目录路径(如果使用到的话)。
layoutString“​layout.htmlpage”​操作布局
viewString操作视图

目前已经有一些预定义好的布局,例如​layout.json​或​layout.xml​。这些预定义的布局可用于产生与HTML不同效果的操作。您可以在app/​views/​directory目录下浏览预定义的布局,甚至可以创建自己的布局。

有时为了保持控制器的完整性而单独重定义部分操作的视图是有必要的。这种情况下只需要把必要的视图或局部文件放在组件的​views​目录下即可。

请参考Reference部分中的动作控制器文件示例。请尽情探索现有Zabbix操作的源代码,位于app/​目录中。

Module.php

这个可选的PHP文件负责模块初始化和事件处理,’​Module’​类应在此文件中定义,包括扩展基类​\Core\CModule​。’​Module’​类必须遵循manifest.json配置文件中的命名规范。

  1. <?php
  2. namespace Modules\Example;
  3. use Core\CModule as BaseModule;
  4. class Module extends BaseModule {
  5. ...
  6. }

详情请参考Module.php在Reference中描述的部分。

参考示例

本章节包含前面几个章节中介绍的不同组件元素的基础版本。

manifest.json

  1. {
  2. "manifest_version": 1.0,
  3. "id": "example_module",
  4. "name": "Example module",
  5. "version": "1.0",
  6. "namespace": "Example",
  7. "author": "John Smith",
  8. "url": "http://module.example.com",
  9. "description": "Short description of the module.",
  10. "actions": {
  11. "example.something.view": {
  12. "class": "SomethingView",
  13. "view": "module.example.something.view"
  14. },
  15. "example.something.create": {
  16. "class": "SomethingCreate",
  17. "layout": null
  18. },
  19. "example.something.delete": {
  20. "class": "SomethingDelete",
  21. "layout": null
  22. },
  23. "example.something.export.xml": {
  24. "class": "data_export/ExportAsXml",
  25. "layout": null
  26. },
  27. "example.something.export.excel": {
  28. "class": "data_export/ExportAsExcel",
  29. "layout": null
  30. }
  31. },
  32. "config": {
  33. "username": "john_smith"
  34. }
  35. }

Module.php

  1. <?php declare(strict_types = 1);
  2. namespace Modules\Example;
  3. use APP;
  4. use CController as CAction;
  5. /**
  6. * 有关其他参考,请参阅Core\CModule类。
  7. */
  8. class Module extends \Core\CModule {
  9. /**
  10. * 组件初始化。
  11. */
  12. public function init(): void {
  13. // 初始化主菜单(CMenu类实例)。
  14. APP::Component()->get('menu.main')
  15. ->findOrAdd(_('Reports'))
  16. ->getSubmenu()
  17. ->add((new \CMenuItem(_('Example wide report')))
  18. ->setAction('example.report.wide.php')
  19. )
  20. ->add((new \CMenuItem(_('Example narrow report')))
  21. ->setAction('example.report.narrow.php')
  22. );
  23. }
  24. /**
  25. * 事件句柄,在执行操作前触发。
  26. *
  27. * @param CAction $action 负责当前请求的操作实例。
  28. */
  29. public function onBeforeAction(CAction $action): void {
  30. }
  31. /**
  32. * 事件句柄,应用退出时触发。
  33. *
  34. * @param CAction $action 负责当前请求的操作实例。
  35. */
  36. public function onTerminate(CAction $action): void {
  37. }
  38. }

操作控制器

  1. <?php declare(strict_types = 1);
  2. namespace Modules\Example\Actions;
  3. use CControllerResponseData;
  4. use CControllerResponseFatal;
  5. use CController as CAction;
  6. /**
  7. * 操作组件示例。
  8. */
  9. class SomethingView extends CAction {
  10. /**
  11. * 初始化操作。Zabbix核心调用方法。
  12. *
  13. * @return void
  14. */
  15. public function init(): void {
  16. /**
  17. * 禁用SID(Sessoin ID)验证。会话ID验证只能用于涉及数据修改的操作,如更新或删除操作。
  18. * 在这种情况下,必须在URL中显示会话ID,这样一旦会话过期,URL就会立即过期。
  19. */
  20. $this->disableSIDvalidation();
  21. }
  22. /**
  23. * 检查并清除用户输入参数。Zabbix核心调用方法。 如果返回false,则执行停止。
  24. *
  25. * @return bool true on success, false on error.
  26. */
  27. protected function checkInput(): bool {
  28. $fields = [
  29. 'name' => 'required|string',
  30. 'email' => 'required|string',
  31. 'phone' => 'string'
  32. ];
  33. // 只有经过验证的数据才能进一步使用$this->hasInput()和$this->getInput()。
  34. $ret = $this->validateInput($fields);
  35. if (!$ret) {
  36. $this->setResponse(new CControllerResponseFatal());
  37. }
  38. return $ret;
  39. }
  40. /**
  41. * 检查用户是否具有执行此操作的权限。Zabbix核心调用方法。
  42. * 如果返回false,则执行停止。
  43. *
  44. * @return bool
  45. */
  46. protected function checkPermissions(): bool {
  47. $permit_user_types = [USER_TYPE_ZABBIX_ADMIN, USER_TYPE_SUPER_ADMIN];
  48. return in_array($this->getUserType(), $permit_user_types);
  49. }
  50. /**
  51. * 准备视图的响应对象。Zabbix核心调用方法。
  52. *
  53. * @return void
  54. */
  55. protected function doAction(): void {
  56. $contacts = $this->getInput('email');
  57. if ($this->hasInput('phone')) {
  58. $contacts .= ', '.$this->getInput('phone');
  59. }
  60. $data = [
  61. 'name' => $this->getInput('name'),
  62. 'contacts' => $contacts
  63. ];
  64. $response = new CControllerResponseData($data);
  65. $this->setResponse($response);
  66. }
  67. }

操作视图

  1. <?php declare(strict_types = 1);
  2. /**
  3. * @var CView $this
  4. */
  5. $this->includeJsFile('example.something.view.js.php');
  6. (new CWidget())
  7. ->setTitle(_('Something view'))
  8. ->addItem(new CDiv($data['name']))
  9. ->addItem(new CPartial('module.example.something.reusable', [
  10. 'contacts' => $data['contacts']
  11. ])
  12. ->show();