📜 ⬆️ ⬇️

Creating loadable Zabbix modules on the example of adding the Modbus protocol

In the Zabbix 2.2 version, loadable modules were added, which allowed us to expand the capabilities of the system at a new level. “Why is this needed?”, You ask, because it was always possible to run external scripts and programs from Zabbix. Of course, first of all, this is speed - modules, like Zabbix itself, are written in C and, with the right approach, work as fast as possible, unlike external programs that need to be run on each poll. Many may be frightened by the need to write code, but today I want to show you that everything is not so difficult.

For example, we will write a module that will allow Zabbix to collect information from devices operating according to the industrial automation protocol, which is widely used in the world - Modbus, and we will take readings of temperature sensors using it, as well as receive electricity parameters from the Mercury 230 meter. portal share.zabbix.com , where users can share their work on Zabbix.



A few words about Modbus


Modbus is a very common protocol used in industrial automation networks (factories, workshops, other industrial facilities, various engineering systems — everywhere you can stumble upon Modbus). The protocol was developed back in 1979 by Modicon, and over the following decades, thanks to its advantages, Modbus was implemented on a huge number of different devices:
')
Simplicity
Modbus is very simple. This allows you to run and implement it on the most simple and cheap pieces of iron. Nor does it discourage developers from using them in their solutions.

Ethernet support
Modbus was on time prepared for the widespread penetration of Ethernet networks, including industrial facilities, and now it works not only via serial RS-232, RS-485 (Modbus RTU / ASCII) lines, but also via TCP / IP (Modbus TCP) .

Openness
All specifications of the protocol are open and accessible to all who wish to use it in their decisions, without any license fees to anyone.

Now I’ll say a few words about how Modbus works. Data is accessed in the device’s memory through special logic tables: Discrete Input, Coils, Input Registers, Holding Registers . In the first two data are presented in the form of single bits, in the last two - in the form of 16-bit words:
TableData typeAccess
Discrete input1 bitonly reading
Coils1 bitread and write
Input Registers16 bitsonly reading
Holding registers16 bitsread and write

The first two tables can be used to work with simple discrete values: “open-closed”, “accident is not an accident”, etc. And Input and Holding Registers can be used for everything else. Moreover, the problem when the data does not fit in 16 bits is easily solved by using two adjacent registers. So you can work with 32bit floats or long.
Data elements in Modbus can be accessed through pass-through addressing from 0 to 65535 across all tables.

Data access may not overlap, and each table leads to a separate memory block:



Similarly, several tables can be used to access the same data:



For example, if we have a controller with 16 dry contacts, and we want to know how it is now, closed or open? Then we can take the values ​​of all 16 contacts at once through the Input Registers table, or read all the contacts in turn through the Input Discrete table.

If Modbus devices are on a serial line (RS-485), then each device in Modbus has its own unique address (slave id, from 1 to 247), the polling device is always one and is called a master. In the case of a Modbus TCP device, an IP address is sufficient.

Where do we place Zabbix here with our future module?

We will be able to place it as a Modbus master by connecting to the RS-485 network with controllers through a converter, or working with devices that support Modbus TCP directly:



Setting up the environment


Let's prepare a small test stand that will help us test our Modbus module in Zabbix. We will take the equipment from the domestic manufacturer Aries , whose input-output devices will allow us to control the temperature, various discrete sensors, and power supply parameters. Also, as a bonus, we will take readings from the common three-phase electric meter Mercury 230R with the help of the DU-1M MAX-Logic converter, which can turn the embedded Mercury protocol into a Modbus. All these devices are connected to the RS-485 bus, to which Zabbix will gain access as a master via an RS232 / RS485 converter.

Configuring Aries modules for Modbus operation is described in the device documentation. In general, here is what you need to do:
Through the Aries configurator, connecting to all modules in turn through the RS232 / RS485 converter:


For DU-1M MAX-Logic (Mercury 230 ) everything is a bit more complicated: First, carefully follow the instructions on the DU-1M to connect to the Mercury 230 counter, then instead of using a cozy graphical configurator like in Aries we will use the console utility modpoll , sending commands to the configuration registers to configure pairing with the counter (phew!).

Here in the end what happens:



And this is how it looks live:



Having assembled a stand, we will test the performance with the help of modpoll , for example, having connected a temperature sensor to the MB110-2A, we will poll it through register 10, as stated in the documentation for the device:



Or remove the energy consumption of the meter Mercury at the rate of T1:



As we can see, there is a connection, and data is collected. It is time to deliver this data to Zabbix.

Writing module


How to write a module for Zabbix?

As in the case of running scripts through a UserParameter, you first need to think about how the item key will look like in Zabbix itself. Let's start with this, in our case it will be such a key:

modbus_read_registers[ <connection>, <slave_id>, <reg_to_read>, <modbus_function>, [<datatype>],[<endiannes>],[<first_reg>] ] 

Inside this key, we will pass four required and three optional parameters:


So, come up with, great, now let's go directly to the module. In fact, we need to write a function to collect data from the Modbus registers and add a special Zabbix interface to it from the following functions:
two required functions:

 int zbx_module_api_version(void); int zbx_module_init(void); 

And also three optional:

 ZBX_METRIC *zbx_module_item_list(void); void zbx_module_item_timeout(int timeout); int zbx_module_uninit(void); 

But we completely copy them from the dummy.c example, which is kindly described in the documentation on Zabbix. Great, saved time and effort, we go further.

And then the question is how to write the collection function itself? By the way, let's call it this way:

 int zbx_modbus_read_registers(AGENT_REQUEST *request, AGENT_RESULT *result) 

And here the work is reduced to a minimum, because in order not to screw up the implementation of the Modbus protocol and not reinvent the wheel, we will take the C library, which already does all this well: libmodbus .

Using the libmodbus interface, we will only have to validate the incoming data from Zabbix, then run the polling functions from libmodbus , and then convert the resulting values ​​into Zabbix data types and return them. Well, or return an error.

The rest of the details that I missed can be found here .

And I once again emphasize a couple of key points:

1) If the function is successfully executed, the result is put into * result via macros:

For example:

 SET_DBL_RESULT(result, modbus_get_float(temp_arr)); 

And the function itself should return SYSINFO_RET_OK.

2) If something went wrong, then you need to return SYSINFO_RET_FAIL , and in * result put an error message that we can see in the Zabbix web interface:

 SET_MSG_RESULT(result, strdup("Check datatype provided.")) 



3) It is very important to remember to validate all incoming input parameters. Otherwise, the fall of the module will lead to the fall of the Zabbix server or agent itself.

4) Remember that Zabbix launches many parallel polling processes. So if you are going to work with resources that do not tolerate simultaneous access (file or serial port in our case), then you need to implement control of this access on your own. I did this through semaphores, which made the task a bit more complicated, but fortunately this need not always be done.

5) Since the external libmodbus library is used, it must be installed on the system where the Zabbix module will be used, for this:

 wget http://libmodbus.org/releases/libmodbus-3.1.2.tar.gz tar zxvpf libmodbus-3.1.2.tar.gz cd libmodbus-3.1.2 ./configure make make install ldconfig 

As a result, we got the following code for the module:

Compile it and add the resulting .so file to Zabbix Server or Zabbix Agent. Here it is only important to remember that the module depends on some headers of Zabbix itself. Therefore, if you want to collect everything separately from Zabbix, then copy at least zbxtypes.h, module.h, sysinc.h from the Zabbix sources to yourself. Well, that's done.

Stand testing


For example, the compiled file libzbxmodbus.so will be uploaded to Zabbix Server, for this we add in the Zabbiks config file:

 LoadModulePath = /usr/local/lib LoadModule = libzbxmodbus.so 

Add the zabbix user to the dialout group to access the serial port:

 usermod -a -G dialout zabbix 

... and restart Zabbix:

 /etc/init.d/zabbix-server restart 

If everything is successfully restarted, then go to Zabbix and create a template for our Modbus devices.

Creating a template


To begin with, we will create data elements for the 110-224.2A to read the readings of the temperature sensor:



Where is the key ( key ):

 modbus_read_registers[{$MODBUS_PORT},{$MODBUS_SLAVE},10,3,f,1,0] 


More on how to choose the parameters here . In addition, you must specify:

Type
Simple check - if the module is loaded on Zabbix Server and Zabbix Proxy,
Zabbix agent - if the module is loaded on Zabbix Agent

Data Type ( Type of information ):
in our case, this is Numeric (float) .

Creating a host


Create a network node , define macros, as agreed:



And attach the template that you just created:



Data


Go to the section Latest data and can begin to observe the data that we managed to collect from the sensor:



And by creating patterns for the remaining device registers, we get the following picture:



Well, then we can create triggers , graphics , complex screens in the templates at will.

Conclusion


Using loadable modules, Zabbix developers have given us an interface for creating really fast and well-integrated extensions with Zabbix. Yes, the possibilities are so far limited by the creation of new types of data elements, and, for example, it will not be possible to add a new trigger function ( ZBXNEXT-2650 ), but wait and see.

So that the modules do not roll all over the Internet, a second important step was taken towards the organization of user solutions - share.zabbix.com - a single repository created for the exchange of templates, modules and other extensions for Zabbix. Looking for a pattern under the sly piece of iron? Or have you written a template for a tricky piece of hardware and are ready to share it with the rest? Looking for a solution on how to monitor PostgreSQL ? Or Docker ?

Come and join us! Well, our module is already there too.

Source: https://habr.com/ru/post/268119/


All Articles